【技术点】Unity的热更新sLua
发表于2016-10-01
需求环境
在【解决方案系列】中,我们需要考虑到我们能预估的项目问题,在现有的工作习惯中,赶工期是常见的一种现象,而这种现象的后果,就导致了测试的不完全,线上项目bug的频繁出现。
为了对线上的BUG进行Fix,我们就需要一定的技术手段,甚至通过游戏初期架构去解决这个影响游戏质量的重要问题。
这是一篇【技术点】文章,同时吸取了之前一篇的文章,我们就不进行复杂的项目架构,只对技术点进行简单的介绍,其他有关但又与主题无关的内容,我们就浅浅跳过。
逻辑热更新
在以往的Unity版本中,有多种热更新的方案留存于世,从之前的uLua到C# Ligh,再到后来的Tolua,Slua等等,甚至早起的PlayMaker都能解决逻辑热更新的问题。
而本篇,基于作者项目在使用的情况,我们将会使用Slua作为我们【解决方案】系列的逻辑热更新方案。
而多种热更新语言的比较,大家可自行谷歌,也可通过该篇进行一定了解:http://blog.csdn.net/langresser_king/article/details/45112519
插件信息
1、插件网站:http://www.slua.net/
2、插件GitHub:https://github.com/pangweiwei/slua/wiki
在插件的GitHub中,有中文的帮助,我们只用简单的将插件导入到工程中,就可以开始使用了。为了让调试变得方便,我们需要进行一些小改动。
使用方法
在查看此节前,确保你已经查看了该插件的基础信息。
sLua加载:
在原生下载的sLua插件中,作者把Lua文件放在Resource目录中,并去掉了后缀名,在我使用的过程中,没有后缀名的文件无法被Vs有效识别,为了方便的调试,我们就需要进行一些准备工作。
sLua已经考虑到了这种情况,所以他提供了方便的接口,供给我们进行更改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | /// /// 从resouce目录加载Lua脚本,并初始化 /// ///Resouce下的目录 ///是否强制读取 public SLua.LuaTable RsLoad( string _rsPath) { Log( "加载:" + _rsPath); if (mluasvr.inited == false ) { Log( "还未初始化,等待初始化后加载:" + _rsPath); waitLoadLua= _rsPath; return null ; } if (luaDict.ContainsKey(_rsPath)) return luaDict[_rsPath]; //设置执行脚本 //LuaState ls_state = new LuaState(); //设置脚本启动代理 LuaState.loaderDelegate= (( string fn) => { //获取Lua文件执行目录 string file_path = Directory.GetCurrentDirectory() + "/Assets/Resources/" + fn; file_path= file_path.Replace( '/' , '\' ); file_path= file_path.Replace( '.' , '\' ); Log( "准备加载脚本:" + file_path); var file = File.ReadAllBytes(file_path + ".lua" ); if (file == null ) LogError( "加载Lua脚本失败,不存在的Lua脚本" ); else Log( "加载脚本成功.文件大小:" + file.Length); return file; }); //通过刚才设置的代理方法加载,就会从Res目录下读取带.lua的文件了 var self = (LuaTable)mluasvr.start(_rsPath); if (self != null ) { luaDict.Add(_rsPath, self); return self; } LogError( "脚本初始化错误,脚本:" + _rsPath); return null ; } |
有了上面这一段代码后,我们就可以直接从Resource目录下调用带有.lua后缀名的lua文件。
热调试:
在我们调试Lua文件时,我们不希望频繁重启客户端,因为Unity编译一次需要很久时间,所以如果可以动态的将已经加载好的Lua文件重新加载,我们就进行快速Lua逻辑调试。
在上面的代码中,为了确保效率,我们将Lua对象放到了LuaDict中,避免了重复调用,而函数本身是可以重新加载Lua文件的,所以我们只需要把Lua文件从Dict中清除就好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | /// /// 移除脚本,移除后,会重新加载,这样就可以热更新了。 /// 平时用缓存里的,性能就没那么差了 /// public void UnstallLuaScripts() { luaDict.Clear(); } /// /// 卸载指定脚本 /// ///脚本目录+名字 public void UnstallLuaScript( string _name) { if (luaDict.ContainsKey(_name)) luaDict.Remove(_name); } |
该有一些其他初始化的过程,不过都是些逻辑问题,大家可以在DJLuaManager.cs文件中插件相关代码。
测试:
有了上面简单的加载代码后,我们就可以测试Lua脚本了。在工程的10-1sLuaTest 场景中,我们能找到测试工具TestTool,上面挂在了脚本Behavior3JSTest.cs ,这是我自己用来测试Lua代码的小脚本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | using UnityEngine; using System.Collections; public class Behavior3JSTest : MonoBehaviour { public bool 加载 = true ; public string loadFunction; //"/DJLua/Behavior3Js/main" public bool 运行一次 = false ; public string runFunction; //testbt public bool 循环执行 = false ; public string loopFunction; void Update() { if (加载 == true ) { 加载 = false ; DoSomething(); } if (运行一次 == true ) { runOnce(); 运行一次 = false ; } if (循环执行 == true ) { runex(); } } void DoSomething() { DJLuaManager.GetInstance().UnstallLuaScripts(); DJLuaManager.GetInstance().RsLoad(loadFunction); } void runOnce() { DJLuaManager.GetInstance().ExecuteFunc(loadFunction, runFunction); } void runex() { DJLuaManager.GetInstance().ExecuteFunc(loadFunction, loopFunction); } } |
每次加载的时候会卸载之前的脚本,而每次点击运行一次的时候,就会执行指定的函数。而在界面上,则有方便调试的窗口供给我们输入内容。
当我们输入了正确的信息后,就可以得到反馈的结果。
而当我们修改了Lua代码后,只用点击加载,不用重启客户端,就可以看到执行的效果。
当然,还有一个小小的Lua代码,文件名字叫sLuaTest.lua:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | -- -- User: John -- Date: 2016年10月1日 星期六 农历九月初一 -- Des: lua的测试脚本 local mClass = { } function main() Debug.Log( "我被加载了" ); return mClass end -- 节点被单击 function mClass:testClick() Debug.Log( "我被点击了" ) end |
结束语
这就是sLua在Unity3d中的快速搭建,因为是【技术点】文章,稍微介绍下,相信大家通过文档能快速搭建出自己的项目。
而对于Lua的熟悉,可以使用《Lua 语言 15 分钟快速入门》来解决前期问题。