让你的游戏支持热更新 - 在Unity3D中使用lua
1、在Unity中引入lua的主要原因:
1.1,Unity不支持代码热更新
C#在ios平台不支持热更新,在Android平台热更新也比较麻烦,而lua支持热更新。
1.2,为了能够前后台复用关键逻辑代码
Unity前台使用C#,后台使用C或C++,属性计算和战斗系统代码无法复用,通过lua可以方便实现前后台关键逻辑代码共用。
2、lua插件的选择:
2.1 UniLua
云风团队的作品,纯C#实现,但使用起来很麻烦,比较鸡肋。
2.2 uLua
uLua是 Lua 、LuaJIT 、LuaInterface的集合,Unity主流的lua插件, AssertStore上售价40美刀。 C#调用lua脚本和从lua脚本中访问C#对象都方便,接下来我们简单了解一下uLua,然后通过一个简单的Demo来深入了解uLua的使用。
3、C#调用lua脚本
基本保持了和C++相同的调用方式,对于用过tolua++的同学非常亲切
// 初始化lua脚本 // FLuaMgr.Instance.Init();
// 调用Test.lua中的 lua脚本函数// FLuaMgr.Instance.PCall("Test.ChangeMyClassProperty"); |
4、lua访问C#对象属性及函数
uLua利用.Net的反射机制,直接从dll中导出C#类定义。
这一块功能是通过Lua.cs实现的,具体实现机制比较复杂,我们看一个简答的例子:
4.1 导入C#类
UnityEngine = luanet.UnityEngine System = luanet.System
--- UnityEngine Classes--- Debug = UnityEngine.Debug GameObject = UnityEngine.GameObject Camera = UnityEngine.Camera Transform = UnityEngine.Transform
--- Our Classes --- luanet.load_assembly('Assembly-CSharp') MyClass = luanet.import_type('MyClass') |
通过这几句代码,我们就将UnityEngine的Debug、GameObject、Camera、Transform类导入进来了,此外将我们自定义的C#类MyClass也导入进了lua。这比用tolua++导出C++对象简单多了。
4.2 在lua使用使用C#导入类
local myClass = GameObject.Find("MyClass"):GetComponent("MyClass");
myClass.DisplayName = "ChangedName From Lua"; local box1 = myClass:DrawBox(1); |
GameObject .Find和GetComponent是Unity常用函数,通过uLua可以在lua中直接访问,这是uLua的重要优势。
此外我们定义类MyClass的属性和函数也可以同样方便的调用,这样扩展起来非常方便。
5、加载lua脚本的优化
创建一个LuaState, 然后使用LuaState的DoString就可以加载lua脚本;
LuaState luaState = new LuaState(); luaState.DoString("print('hello world')"); |
这样虽然可以加载lua,但是这并不能满项目需求,笔者封装了一个FLuaMgr
在FLuaMgr的Init函数中加载lua文件下的所有lua文件:
// 直接读取TextAsset// private bool DoDirFilesByTextAsset(string path) { TextAsset[] luaCodes = Resources.LoadAll for (int i = 0; i < luaCodes.Length; i++) { string fileName = luaCodes[i].name; //Debug.Log("FileName=" + fileName); int prefabEnd = fileName.LastIndexOf(GenLuaFileEnding); // 判断是否是lua文件 // if (prefabEnd <= 0) { FDebug.LogWarning("FileName=" + fileName + " is not a lua file!"); continue; }
DoString(luaCodes[i].text, fileName); }
// 卸载lua脚本文件 // Resources.UnloadUnusedAssets(); return true; } |
5.1 Unity 只能识别.txt文件文件的解决办法
在Assets/Lua 文件夹编辑lua脚本,然后通过ConvertToLuaTxt 转化到Assets/LuaGen文件夹下。具体内容参考Demo中的ConvertToLuaTxt.cs
6、Unity中嵌入lua的Demo介绍
将附件中的uLuaExample.unitypackage 导入到一个空的Unity项目,然后打开LuaTest.unity,直接运行,lua脚本调用MyClass的函数绘制若干个矩形、球和圆柱体。
-- 调用C#对象的函数 function DrawSomeBoxes() -- 直接调用GameObject的导出函数Find,类导出查看ImportClass.lua local myClass = GameObject.Find("MyClass"):GetComponent("MyClass");
-- 调用我们的C#类MyClass.DrawBox(float size) local box1 = myClass:DrawBox(1); box1.transform.position = Vector3(-5,-2.5,0);
local box2 = myClass:DrawBox(1.25); box2.transform.position = Vector3(-5,-1.0,0);
local box3 = myClass:DrawBox(1.5); box3.transform.position = Vector3(-5,1.5,0); end |
执行效果图:
创建对象和位置排列逻辑由Test.lua实现。
7、lua执行效率
7.1 加载所有lua脚本的消耗
笔者所在的项目有13个lua文件,lua脚本总行数1127行。
在华为荣耀3手机上初始化lua平均耗时270ms。
7.2 执行简单的lua函数的效率:
function SimpleLuaFunc() local id = 1; if (id < 1) then print("id < 1"); end |
一次简单的lua调用平均耗时9微秒,性能在可接受的范围内。