当前位置:   article > 正文

xLua(九)——实战_xlua dispose

xlua dispose

一:使用xLua的步骤

——导入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文件夹同级)


二:xLua的一些坑

——没有导入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补丁脚本是这样的:

  1. xlua.private_accessible(CS.Text) --这样才可以使用类中的私有字段,属性,方法
  2. xlua.hotfix(CS.Text,'Method',function(self,value)
  3. value=self.x
  4. local _go=CS.UnityEngine.Object.Instantiate(self.prefabGo,self.UnityEngine.Vector3(value,0,0),CS.UnityEngine.Quaternion.identity)
  5. _go.transform:SetParent(CS.UnityEngine.GameObject.Find("Canvas").transform);
  6. end)

 

 


——对于CS脚本中需要修改的地方在补丁脚本中执行,不需要修改的地方仍然在CS脚本中执行(这种方式尽量少使用,会降低性能)
例如在CS脚本中:

对应的Lua补丁脚本中我们希望go=GameObject.Find("MyGo").gameObject这句话仍然在CS脚本中执行,补丁脚本中只修改value的值:

  1. local util=require 'util'
  2. xlua.private_accessible(CS.Test)
  3. util.hotfix_ex(CS.Test,'Method',function(self)
  4. self:Method()
  5. self.value=5
  6. 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中的向下取整函数将小数变为整数

  1. xlua.hotfix(CS.Test,'Start',function(self)
  2. self.num=CS.UnityEngine.Mathf.FloorToInt(CS.UnityEngine.Random.Range(0,5))
  3. end)

 

 

——对于CS脚本中的泛型方法
例如在CS脚本中:

对应的Lua补丁脚本是这样的:

  1. xlua.hotfix(CS.Test,'Start',function(self)
  2. local _go=CS.UnityEngine.Object.Instantiate(self.go)
  3. _go:GetComponent('Rigibody').useGravity=false
  4. end)

 

 

——补丁脚本中加载AB包资源(例如加载一个游戏物体)
首先前期在CS中写好扩展的接口,提供后期有需要的时候去调用:

  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. public class Extension
  4. {
  5. private static Dictionary<string, GameObject> prefabDict;
  6. //加载资源
  7. public static void LoadResource(string abName, string resName)
  8. {
  9. AssetBundle ab = AssetBundle.LoadFromFile(@"Assets/MyAssetBundles/" + abName);
  10. GameObject _go = ab.LoadAsset<GameObject>(resName);
  11. if (prefabDict == null)
  12. {
  13. prefabDict = new Dictionary<string, GameObject>();
  14. }
  15. prefabDict.Add(resName, _go);
  16. }
  17. //得到物体
  18. public static GameObject GetGo(string resName)
  19. {
  20. return prefabDict[resName];
  21. }
  22. }

之后在Lua补丁脚本中实例化这个物体

  1. --在Start或者Awake中先加载出这个资源
  2. CS.Extension.LoadResource('abName','resName')
  3. --当需要的时候再实例化出来
  4. local _go=CS.Extension.GetGo('resName')
  5. CS.UnityEngine.Object.Instantiate(_go)

 

 

——对于无法预测的CS类(无法预测的功能)
例如当前有一个物体,我们无法预测它后期会添加什么功能,我们可以给它添加一个空类,空类里定义一些可能会用到的空方法:

  1. using UnityEngine;
  2. using XLua;
  3. [Hotfix]
  4. public class Empty : MonoBehaviour
  5. {
  6. private void Start()
  7. {
  8. }
  9. private void Update()
  10. {
  11. }
  12. private void Method1()
  13. {
  14. }
  15. private void Method2()
  16. {
  17. }
  18. }

之后需要的时候在Lua补丁脚本中重写打了补丁的空类中提供的方法:

  1. xlua.private_accessible(CS.Empty)
  2. xlua.hotfix(CS.Empty,'Start',function(self)
  3. end)
  4. xlua.hotfix(CS.Empty,'Update',function(self)
  5. end)
  6. xlua.hotfix(CS.Empty,'Method1',function(self)
  7. end)

 

 

——对于CS中的协程
例如在CS脚本中:

对应的Lua补丁脚本是这样的:

  1. local util=require 'util'
  2. xlua.hotfix(CS.Test,{
  3. Start=function(self)
  4. return util.cs_generator(function()
  5. coroutine.yield(CS.UnityEngine.WaitForSeconds(5))
  6. CS.UnityEngine.MonoBehaviour.print('Wait For 5 Seconds')
  7. end)
  8. end
  9. })

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/82267
推荐阅读
相关标签
  

闽ICP备14008679号