赞
踩
——导入xLua插件
其实xLua本质就是一个Unity工程,把Asset中的文件导入到Unity工程中就搞定了(导入之后编辑器菜单栏会扩展出一个XLua选项)
——添加宏
File——Build Settings——Player Settings——Other Settings——Scriptsing Define Symbols——输入HOTFIX_ENABLE——按下Enter
检测添加是否成功:选择之前菜单栏的XLua选项,发现多出了一个Hotfix Inject In Editor选项
——重新生成代码&重新注入代码
每当CS代码发生修改时,就必须点击重新生成代码(生成成功后控制台输出:finished! use XXX ms)
和将热修复补丁注入到Unity编辑器中(注入成功后控制台输出:XXX had injected!)
如果点击Hotfix Inject In Editor时报错:please install the Tools
则需要将xLua插件中的Tools文件夹也导入Unity工程中(注意与Assets文件夹同级)
——没有导入Tools文件夹
如果报以下错误:please install the Tools
则需要将xLua插件中的Tools文件夹也导入Unity工程中(注意与Assets文件夹同级)
——Untiy工程目录不能带有中文
如果报以下错误:Exception: project path must contain only ascii character
则需要将项目放在一个没有中文的目录下
——没有重新生成代码&重新注入代码&没有添加热修复标签
如果报以下错误:LuaException: xlua.access, no field __Hotfix0_XXX
一种情况是没有重新生成代码并重新注入代码到Unity编辑器中
或者是没有给XXX类添加[Hotfix]的标签
——C#仍指向Lua虚拟机中某个function的委托
如果报以下错误:InvalidOperationException: try to dispose a LuaEnv with C# callback!
则需要使用xlua.hotfix(class, method, nil)删除(注入的时候是使用xlua.hotfix(class, method, func)注入的)
建议开发过程中单独写一个Lua脚本进行所有委托的销毁,将加载此脚本的方法写在释放Lua虚拟机的前面
大概说一下热补丁的实现原理,当我们给一个类打补丁后使用xlua.hotfix为类中的方法重写时,这个重写的Lua方法会注册到Lua虚拟机的一个委托中,当我们释放Lua虚拟机时,这个Lua方法并没有被释放所以会报错
关于热补丁的实现原理,推荐知乎上xLua作者写的:xLua热补丁实现原理
另外推荐一篇介绍中间语言IL的文章:中间语言IL
——访问成员方法时参数中没有传入调用者自身
如果报以下错误:LuaException: invalid arguments to XXX!
则说明XXX方法是一个成员方法,参数中需要传入调用者自身
例如SetParent方法是一个成员方法,可以使用冒号去访问或者在参数中传入调用者自身
——打上[LuaCallCSharp]或者[LuaCallCSharp]标签
——报一些奇怪的错误
删除Example文件夹,清空代码重新生成注入
——正常开发C#代码
——在所有可能出现问题的类上打上[Hotfix]标签(不能后期发布后再打标签)
——在所有Lua调用C#的方法上打上[LuaCallCSharp]标签,在所有C#调用Lua的方法上打上[CSharpCallLua]标签
——修改bug时只需要修改Lua文件,修改资源时只需要更新ab包,用户只需要去下载Lua文件和ab包即可
——对于CS脚本中的大多数方法
例如在CS脚本中:
对应的Lua补丁脚本是这样的:
- xlua.private_accessible(CS.Text) --这样才可以使用类中的私有字段,属性,方法
-
- xlua.hotfix(CS.Text,'Method',function(self,value)
- value=self.x
- local _go=CS.UnityEngine.Object.Instantiate(self.prefabGo,self.UnityEngine.Vector3(value,0,0),CS.UnityEngine.Quaternion.identity)
- _go.transform:SetParent(CS.UnityEngine.GameObject.Find("Canvas").transform);
- end)
——对于CS脚本中需要修改的地方在补丁脚本中执行,不需要修改的地方仍然在CS脚本中执行(这种方式尽量少使用,会降低性能)
例如在CS脚本中:
对应的Lua补丁脚本中我们希望go=GameObject.Find("MyGo").gameObject这句话仍然在CS脚本中执行,补丁脚本中只修改value的值:
- local util=require 'util'
-
- xlua.private_accessible(CS.Test)
- util.hotfix_ex(CS.Test,'Method',function(self)
- self:Method()
- self.value=5
- end)
util是xLua提供的一个库,因为require的加载是加载同级目录的脚本,所以需要将util.lua.txt复制到补丁脚本的同级目录下
util.lua.txt的位置在xLua-master\Assets\XLua\Resources\xlua中
——对于CS脚本中的Start生命周期函数
有些时候我们为CS脚本的方法打补丁时,会发现补丁代码重写的方法并没有执行,仍然执行的是CS中的原方法,这是因为加载Lua脚本的代码(也就是补丁代码)没有最先被调用,而是在CS原方法执行后再被调用,这样的话补丁方法就失去了作用
例如将加载Lua补丁脚本的代码放在Start方法中,补丁代码重写的也是一个类中的Start方法,这样就有可能先执行CS原方法,补丁方法就失去了作用,简单工程中建议加载Lua补丁脚本的代码放在Awake中,加载Lua脚本的代码最好放在最最最最最前!
——对于CS脚本中的Random.Range
首先转到Random.Range的定义,可以看出它是一个重载方法
接下来在CS脚本和Lua补丁脚本中测试,运行后可以得出xLua默认调用的是返回值为float的Random.Range方法,又因为CS访问Lua文件中的number类型时,会进行自动的类型转换(低类型可以自动转高类型,也就是可以给高类型的值赋予一个低类型的值)因为Lua中的数字类型统一为number,而C#有int,double,float等类型,例如用Lua文件中定义的一个本质为float类型的值去赋值给C#中的int类型的变量,则会输出为0),以上的测试结果也是如此,因为之前CS脚本中定义的num是一个int类型数字,而UnityEngine.Random.Range得到的是一个float类型数字,所以num会赋值为0
1.第一种解决方法:使用Unity中的向下取整函数将小数变为整数
- xlua.hotfix(CS.Test,'Start',function(self)
- self.num=CS.UnityEngine.Mathf.FloorToInt(CS.UnityEngine.Random.Range(0,5))
- end)
——对于CS脚本中的泛型方法
例如在CS脚本中:
对应的Lua补丁脚本是这样的:
- xlua.hotfix(CS.Test,'Start',function(self)
- local _go=CS.UnityEngine.Object.Instantiate(self.go)
- _go:GetComponent('Rigibody').useGravity=false
- end)
——补丁脚本中加载AB包资源(例如加载一个游戏物体)
首先前期在CS中写好扩展的接口,提供后期有需要的时候去调用:
- using UnityEngine;
- using System.Collections.Generic;
-
- public class Extension
- {
- private static Dictionary<string, GameObject> prefabDict;
-
- //加载资源
- public static void LoadResource(string abName, string resName)
- {
- AssetBundle ab = AssetBundle.LoadFromFile(@"Assets/MyAssetBundles/" + abName);
- GameObject _go = ab.LoadAsset<GameObject>(resName);
-
- if (prefabDict == null)
- {
- prefabDict = new Dictionary<string, GameObject>();
- }
- prefabDict.Add(resName, _go);
- }
-
- //得到物体
- public static GameObject GetGo(string resName)
- {
- return prefabDict[resName];
- }
- }
之后在Lua补丁脚本中实例化这个物体
- --在Start或者Awake中先加载出这个资源
- CS.Extension.LoadResource('abName','resName')
-
- --当需要的时候再实例化出来
- local _go=CS.Extension.GetGo('resName')
- CS.UnityEngine.Object.Instantiate(_go)
——对于无法预测的CS类(无法预测的功能)
例如当前有一个物体,我们无法预测它后期会添加什么功能,我们可以给它添加一个空类,空类里定义一些可能会用到的空方法:
- using UnityEngine;
- using XLua;
-
- [Hotfix]
- public class Empty : MonoBehaviour
- {
- private void Start()
- {
-
- }
-
- private void Update()
- {
-
- }
-
- private void Method1()
- {
-
- }
-
- private void Method2()
- {
-
- }
- }
之后需要的时候在Lua补丁脚本中重写打了补丁的空类中提供的方法:
- xlua.private_accessible(CS.Empty)
- xlua.hotfix(CS.Empty,'Start',function(self)
-
- end)
- xlua.hotfix(CS.Empty,'Update',function(self)
-
- end)
- xlua.hotfix(CS.Empty,'Method1',function(self)
-
- end)
——对于CS中的协程
例如在CS脚本中:
对应的Lua补丁脚本是这样的:
- local util=require 'util'
-
- xlua.hotfix(CS.Test,{
- Start=function(self)
- return util.cs_generator(function()
- coroutine.yield(CS.UnityEngine.WaitForSeconds(5))
- CS.UnityEngine.MonoBehaviour.print('Wait For 5 Seconds')
- end)
- end
- })
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。