前言:
一直没有做过Lua相关的商业项目,这次总算有机会得到这个机会,并且对游戏前端用xLua进行了实现。
之前在业余的时间里自己经常练手写一些关于uLua的东西,但真正工作用起来,发现业余玩一玩的练手方式其实还是不够的,需要多实践。
之前所了解的一些热更的方案有LuaJit、uLua、sLua、xLua、L#(C# Light)等,最终我们选了xLua的方案。
原因:
1、据我所知,LuaJit和Lua并不是同一门语言。
2、uLua:据我所知,uLua是当前速度最快的Lua,但它所存在的缺陷就是uLua的分支版本多,造成了不统一的现象。
3、sLua:不熟悉。
4、xLua:作者和云风撕了一场大逼,性能上可能并不如uLua,也可能会让大家产生撞大运编程的即视感,不过由企鹅主导维护,并且只有一个版本,对开发者还算比较友好。
5、L#:李中二是个懒虫,踏马赛克的都不维护了。
每种语言或者插件,都有各自的优势,所以它才会有人用。
xLua的优势:http://www.gad.qq.com/article/detail/24967
GitHub地址:https://github.com/Tencent/xLua
正题:
重点强调:不要在意我的实现方式,因为我还没去做优化,暂时只是简单的做出来,我也是完全靠个人领悟,目前我也没弄到可直接运行供我参考的可热更的Lua资源,我正在努力的做我的第一个关于Lua的商业作品,我也很纠结,说多了都是泪啊。
1、用法和uLua相似,导进来就行,具体用法大家看官方教程吧,xLua下载下来后里面有文档。
2、同样不支持JIT,今天就在通讯的泛型上面有一些困惑,最终问了作者,不支持JIT,原因:为了支持IOS而不能使用JIT
3、Lua调用C#的类型映射类似下图的方式,需要什么加什么(例子在官方工程的ExampleGenConfig.cs里面)。
4、C#调用xLua,观察者模式下传递委托会出现异常,有可能是因为这个委托的类型没有配置(例子在官方工程的ExampleGenConfig.cs里面)。
这个委托类型是我自己加上去的,我在观察者模式上用到这个委托的回调。
5、挂载xLua脚本。
xLua的方案中,unity挂脚本是通过一个LuaBehaviour.cs来作为主控挂载xLua的脚本,按照unity的思维,应该是可以同时挂载多个脚本,所以我对xLua的LuaBehaiour.cs做了一点点的调整。不要在意我的实现方式,因为我还没去做优化,暂时只是简单的做。
void Awake() { LuaEnv luaenvScriptCtrl = new LuaEnv(); if (luaAssetRoot == null) { luaAssetRoot = Assets.InstantiateTextAsset(GameData.gameInfos.selectGameInfo.resPathOf("LuaScriptList.lua")); }//获取一个Lua脚本LuaScriptList.lua,这个脚本里存放的是prefab的名字和脚本名字的键值对,一键可对多值,因为一个GameObject可以挂多个脚本。 luaenvScriptCtrl.DoString(luaAssetRoot.text); string thisName = this.gameObject.name.Replace("(Clone)", ""); thisName = thisName.Replace("(Clone)", "");//去掉克隆字样,括号可能是半角或者全角都作处理 string scriptStrs = luaenvScriptCtrl.Global.Get<LuaTable>("LuaScriptTable").Get<string>(thisName);//从LuaScriptList.lua中取出自己的配置表 string[] scriptArray = scriptStrs.Split(‘|‘);//解析我这个Prefab用哪些lua脚本。 foreach (var s in scriptArray) { if (luaScripts == null) { luaScripts = new List<TextAsset>(); } luaScripts.Add(Assets.InstantiateTextAsset(GameData.gameInfos.selectGameInfo.resPathOf(s))); } //将解析出来需要加上去的脚本统一加上去。 #region 官方例子里的内容,我先括起来。 scriptEnv = luaEnv.NewTable(); LuaTable meta = luaEnv.NewTable(); meta.Set("__index", luaEnv.Global); scriptEnv.SetMetaTable(meta); meta.Dispose(); scriptEnv.Set("self", this); if (injections != null) { foreach (var injection in injections) { scriptEnv.Set(injection.name, injection.value); } } #endregion if (luaScripts != null) { foreach (var item in luaScripts) { luaEnv.DoString(item.text, "LuaBehaviour", scriptEnv); luaAwake += scriptEnv.Get<Action>("awake"); luaStart += scriptEnv.Get<Action>("start"); luaUpdate += scriptEnv.Get<Action>("update"); luaOnDestroy += scriptEnv.Get<Action>("ondestroy"); luaOnEnable += scriptEnv.Get<Action>("OnEnable"); luaOnDisable += scriptEnv.Get<Action>("OnDisable"); }//将xLua的运作方式改成和unity monobehaviour一致。 } if (luaAwake != null) { luaAwake(); } }
剩下的start、update等等函数也需要做一点处理。
// Use this for initialization void Start () { if (luaStart != null) { luaStart(); } } // Update is called once per frame void Update () { if (luaUpdate != null) { luaUpdate(); } if (Time.time - LuaBehaviour.lastGCTime > GCInterval) { luaEnv.Tick(); LuaBehaviour.lastGCTime = Time.time; } } void OnDestroy() { if (luaOnDestroy != null) { luaOnDestroy(); } luaOnDestroy = null; luaUpdate = null; luaStart = null; if (scriptEnv != null) { scriptEnv.Dispose(); } injections = null; } private void OnEnable() { if (luaOnEnable != null) { luaOnEnable(); } } private void OnDisable() { //this.gameObject.SetActive(false); if (luaOnDisable != null) { luaOnDisable(); } }
6、跑起来之前先要做一个wrap操作。
选项1、生成wrap等。
项选2、清除wrap等。
选项3、使用类或者方法的热更:该项功能主要是打补丁,比如替换C#的某个方法或者某个类。
7、跑起来时LuaBehaviour的效果。
使用热更的那个游戏服务器关了,游戏进不去了,下回再补上。
6点30下班都走了,服务端的走了没法开了,真的没加班。
另外公司招会unity+lua的熟手,有大神愿意来吗,薪资保证你满意,虽然薪资不是我说了算。
8、Lua代码
这种代码的写法我真的很难受GameLogic2007.lua.txt。
Singleton = {} function Singleton:new(o) o = o or {} setmetatable(o,self) self.__index = self return o end function Singleton:Instance() if self.instance == nil then self.instance = self:new() end return self.instance end GameLogic2007 = Singleton:Instance() -----以上代码表示我是个单例。 function awake() GameLogic2007.s = HandleGetClientSetting2007 --HandleGetClientSetting2007这里赋值的话也就是可以用GameLogic2007.s来调HandleGetClientSetting2007, end--相当于unity的Awake function start() print("startStart!") local nullBodyContract = CS.NullBodyContract CS.Game.Game2007.BJL.Logic.GameProtocolMgr.instance:SendClientLoadComplete(nullBodyContract) print("startOver!") end--相当于unity的Start function update() end--相当于unity的update function ondestroy() end--相当于unity的ondestroy function OnEnable() print("OnEnableStart!") self.LuaCSAddListener(self,"GameState2007", HandleGameState2007) self.LuaCSAddListener(self,"GetClientSetting2007",GameLogic2007.s) self.LuaCSAddListener(self,"SetGameResult2007", HandleSetGameResult2007) print("OnEnableOver!") end--相当于unity的 OnEnable,观察者模式下我的监听放于此。 function OnDisable() print("OnDisableStart!") self.LuaCSRemoveListener(self,"GameState2007", HandleGameState2007) self.LuaCSRemoveListener(self,"GetClientSetting2007", GameLogic2007.s) self.LuaCSRemoveListener(self,"SetGameResult2007", HandleSetGameResult2007) print("OnDisableOver!") end--相当于unity的 OnDisable,观察者模式下我的释放监听放于此。 HandleGameState2007 = function (f1,f2,f3) print("HandleGameState2007-------------获取到了游戏状态,如果TXT为UTF8,则显示中文------") end--监听的回调函数 local s function HandleGetClientSetting2007(f1,f2,f3) print("HandleGetClientSetting--------我是威少,我获取到了游戏信息-----------") end--监听的调函数 HandleSetGameResult2007 = function (f1,f2,f3) print("HandleSetGameResult2007---------------------!") end--监听的回调函数。
9、更热不支持调用泛型,因为泛型需要JIT的支持。
A:通讯协议这一块,还是需要改的,现在我们只是在游戏前端的逻辑上用xLua实现,实现完了后我们一步一步的改过来,因为我们现在用的泛型,T类型在热更上肯定是不行的。
B:对于通信这一块,我们目前用的是短连接,我个人的想法是数据的传递用字典来传递,反正都是要序列化的,前后端将字典一致就行了。这样就免除了T类型的麻烦。
C:我一个朋友那边通讯是在socket上做了封装,取服务端数据是lua调安卓,安卓调ndk,然后由ndk去对接服务端,好像很高端,但是我们现在这么改肯定费时费力。
下图方式在热更上肯定是不行的,不要泛型,不要泛型,不要泛型,重要的事情说三遍还标红。
10、未完待续,下班回家,下次再继续写。
----------------------------我一直在装逼,却少有装逼成功,请容我继续装逼。