HotFix相当于对已写好的脚本做一个打补丁的操作。
首先,需要在发布前对可能会修改的类前面添加[Hotfix]标识。然后在类中对于要修改的方法前添加[LuaCallCSharp]标识。至于后面这个标识,发现在编辑器模式下是可以不加的,类前面添加[Hotfix]就可以了,不过看官方Git上的FAQ,貌似发布后会报错,这里就先不深究了,就以需要添加为准了。
第一阶段:xlua.hotfix
[Hotfix]
public class HotFixMgr : MonoBehaviour
{
private LuaEnv m_env;
private GameObject Cube, Sphere, Capsule;
void Start ()
{
m_env = new LuaEnv();
m_env.AddLoader(MyLoader);
m_env.DoString("require 'HotFix'");
Cube=GameObject.Find("Cube");
Sphere= GameObject.Find("Sphere");
Capsule = GameObject.Find("Capsule");
}
[LuaCallCSharp]
private void SetParent()
{
Cube.transform.SetParent(Sphere.transform);
Cube.transform.localPosition=Vector3.zero;
}
private byte[] MyLoader(ref string filePath)
{ string absPath = @"C:\Users\xxxx\Desktop" + "/" + filePath + ".lua.txt";
string info = File.ReadAllText(absPath);
return System.Text.Encoding.UTF8.GetBytes(info); }
private void OnDestroy()
{
m_env.Dispose();
}
}
上面涉及到一个自定义Loader的编写,为的是方便处理我们自己路径下的lua脚本。这里关键写lua脚本中的内容。
'HotFix.lua.txt'
xlua.private_accessible(CS.HotFixMgr)
xlua.hotfix(CS.HotFixMgr,'SetParent',function(self)
self.Cube.transform:SetParent(self.Capsule.transform);
--self.Cube.transform.SetParent(self,self.Capsule.transform);
self.Cube.transform.localPosition=CS.UnityEngine.Vector3.zero;
end)
这里重点说两点
1.lua访问类中的私有变量
可以看到上述多了一句xlua.private_accessible(CS.HotFixMgr),作用在于访问HotFixMgr中的私有变量,因为这里Cube等变量都是私有的,直接访问会导致报空。如果本身变量公有,不需要添加这句。
2.Lua中对unity中函数的访问
比如Vector3,在lua中访问时,需要写清楚它所有的父类引用:CS.UnityEngine.Vector3。
某一个属性,可以直接通过.来访问,而某一个方法,就有两种书写方式了。
一种是self.Cube.transform:SetParent(self.Capsule.transform);
一种是self.Cube.transform.SetParent(self,self.Capsule.transform);
这里就是lua中.和:的区别。
到这里最基本的lua修改某个方法就实现了,当然c#中各种类型的方法多了去了,比如重写带参数的,带返回值的,IEnumerator等等。这个稍后继续。
接下来还有一个坑,就是
它会在OnDestroy中调用m_env.Dispose()时报错,就是说我们在lua脚本中修改的SetParent的方法并没有释放掉。这里需要另起一个lua脚本,哪个方法改过,就把哪个方法置为nil就可以了。而这个要在OnDestroy之前调用,所以,在OnDisable()中新加一个。
private void OnDisable()
{
m_env.DoString("require 'HotDispose'");
}
而在'HotDispose'的lua脚本中,加入xlua.hotfix(CS.HotFixMgr,'SetParent',nil)就可以了。
第二阶段:util.hotfix_ex
如果本身方法中大部分都是可用的,只有小部分修改,也就是既想调用原方法,又想在原方法的基础上执行新的方法。
这里需要引入Xlua已经封装好的util.lua.txt,它所在路径为XLua\Resources\xlua。上代码:
local util=require 'util'
util.hotfix_ex(CS.HotFixMgr,'SetParent',function(self)
self:SetParent()
self.Cube.transform:SetParent(self.Capsule.transform);
self.Cube.transform.localPosition=CS.UnityEngine.Vector3.zero;
end)
和xlua.hotfix写法一样,直接在新的方法体中调用self:SetParent()就可以执行C#原来的方法了。这里也是要注意两点。
1.不能在xlua.hotfix中再调用当前要修改的方法
不然会报这个错。
2.这里需要把util.lua.txt脚本放到和当前自定义loader相同的路径下,虽然它本身在工程的Resource目录下,但这里发现是调用不到的,会提示找不到该文件。
第三阶段:修改各式函数(带参,带返回值,构造函数)
1.带参数的方法
这个直接在方法后面添加参数就可以了
xlua.hotfix(CS.HotFixMgr,'ShowNum',function(self,val)
local c=val+3
print(c)
end)
2.带返回值的方法修改,这里引用一个C#中写的枚举。
public enum Fruit
{
Apple,
Banana
}
xlua.hotfix(CS.HotFixMgr,'GetValue',function(self)
return CS.Fruit.Banana
end)
3.构造函数
比如C#代码如下
[Hotfix]
public class Test : MonoBehaviour {
public Test()
{
Debug.Log("222");
}
}
在lua中,通过[.ctor]标识构造函数
xlua.hotfix(CS.Test,{
['.ctor'] = function(self)
print('ccccc')
end
})
这里发现,每次Test ts=new Test(),都会先执行原C#脚本中的构造函数,再执行lua中重写的方法,而不会直接替换掉。