在这里我们使用的是uLua/cstolua技术空间所以提供的UGUI+uLua的热更游戏框,我也只是把我学习和使用这个框架的笔记记录下来而已。
主要使用到的资料有:
学习本框架,首先需要了解lua脚本的基本语法,然后先学习tolua框架,明白C#与lua之间的交互之后,我们再对LuaFramework_UGUI框架进行学习。
关于toLua的学习我们可以直接借助框架中提供的Demo来帮助我们理解,用Unity打开tolua-master工程,在目录ToLua/Example中,就是框架作者为我们提供的学习demo:
例如案例一中就是在C#中调用Lua中的print打印方法打印日志:
using UnityEngine;
using System.Collections;
using LuaInterface;
public class HelloWorld : MonoBehaviour
{
void Start ()
{
LuaState lua = new LuaState();
string hello =
@"
print('hello tolua, 广告招租')
";
lua.DoString(hello, "hello");
lua.CheckTop();
lua.Dispose();
}
}
声明hello这串字符串就是lua脚本的内容,具体调用的办法就是创建一个LuaState对象,相当于一个操作接口,从而在C#中对lua脚本进行操作。此外,关于toLua相较于原本的uLua,实际上就是对交互接口LuaScriptMgr.cs进行了功能拆分,变成了成 ToLua.cs 和 LuaState.cs , 一个为lua 回调C#服务,一个为C#调用lua 服务。
在这里,我们就不一一地对每个案例做赘述,大家自行了解学习,我们这里要讲解的重点是游戏客户端热更框架LuaFramework_UGUI-master。
这个其实是我们用于测试客户端框架而使用到的工具,我们只需要如何使用它来进行测试就行了,至于具体实现,有兴趣的朋友自己研究一下吧。
启动方式有两种,一是使用源码在VS中编译生成可执行文件,二是直接使用包里已编译好的.exe可执行文件,显然第二种办法更为简便一些。文件目录为:ServerFramework-master\Server\bin\Debug\SuperSocket.SocketService.exe,正常启动的结果如下图:
此状态为准备就绪等待客户端的登陆连接,而客户端正常连接时:
打开框架,我们首先来看看其文件目录:
我们可以简单介绍下每个目录的用途。
Examples :框架自带的Demo例子,如果只需要框架的同学,里面的资源可以删除掉。去“疑难解答”里面查看方法。
—Builds:里面都是一些NGUI/UGUI定义的图集啊、Prefab等资源。用于生成assetbundle而准备的资源。
—Editor:里面是例子用到的一个新手引导步骤演示的编辑器脚本。
—Editor Default Resource:目录是新手引导步骤对话框用的的图片资源。
—Rsources:例子里面用于演示的一个内建的GUI容器的Prefab。
—Textures:里面是Buidls目录里面图集的原图文件。
Scenes:里面一个login场景文件也是cstolua自带的性能测试场景文件。
Lua:框架自带的Lua源码目录,用户自定义的Lua脚本也就是放在这里面,最后打包的时候,打包脚本会将其按目录结构生成到StreaminAssets目录里面去,然后在将其上传到游戏的Web服务器上面,用于准备被每个游戏客户端下载更新他们本地的Lua脚本。达到热更目的。
—3rd:里面是第三方的一些插件lua、实例源码文件,比如:cjson、pbc、pblua、sproto等。
—Common:公用的lua文件目录,如define.lua文件,一些变量声明,全局配置等,functions.lua常用函数库,通讯的protocal.lua协议文件。
—Controller:控制器目录,它不依赖于某一个Lua面板,它是独立存活在Luavm中的一个操作类,操作数据、控制面板显示而已。
—Logic:目录里面存放的是一些管理器类,比如GameManager游戏管理器、NetworkManager网络管理器,如果你有新的管理器可以放到里面。
—View:这是面板的视图层,里面都是一些被Unity调用的面板的变量,走的是Unity GameObject的生命周期的事件调用。
Plugins:ulua底层库所在的目录,里面存放的是不同平台的底层库,之所以ulua效率高,就是它是纯c的lua虚拟机,而不是c#解释型的。
—Andriod:安卓lua虚拟机底层库,里面分为armv7-a与Intel x86平台。
—iOS:里面就是苹果lua虚拟机底层库。
—ulua.bundle:里面是Mac机器的底层库。
—x86:里面是Win32/Linux32位机器的lua虚拟机底层库。
—x86_64:里面是Win64/Linux64位机器的lua虚拟机底层库。
Scripts:框架的C#脚本层,之所以这个目录跟lua目录都放在最外层,为了让用户一眼都能找到,明白是什么。
—Common:框架的公用定义类。LuaLoader(跟lua加载有关的类)、与luavm通知unity游戏对象的“LuaBehaviour”桥类。
—ConstDefine:常量定义目录,AppConst(应用常量)ManagerName(管理器名称)NotiConst(通知常量,用于mvc消息通知)。
—Controller:控制器目录,分为StartUpCommand启动控制器,跟常用逻辑控制器。框架接收到启动命令后,直接在启动命令里面注册所有的管理器类。
—Framework:经过修改过的PureMVC的框架文件。
—Manager:Unity提供基础功能的管理器类,音乐、面板、线程、资源等众多管理器。
—Network:网络的常用辅助类,ByteBuffer字节操作封装类,网络协议类,转换器类。
—Utility:常用工具类。
—View:C#用的PureMVC的视图层。
ToLua(低版本的可能是uLua):ulua/cstolua的核心目录,里面还有经过我们修缮后ulua的基础使用例子,用户初学者最佳。
—Core:顾名思义,ulua的核心目录,所有c#与lua的交互都是通过它进行调度的。
—Editor:这是供cstolua去反射定义Wrap文件列表的工具类目录。
—Examples:经过我们修改增加后ulua自带的例子。
—Source:这个是cstolua的核心目录,里面有Base核心目录,与动态生成用于存放LuaWrap类的缓存目录。
原来版本的消息结构为PureMVC,而新版的改成了:
AppFacade + Controller层通讯(修改版)+重写的View层通讯 = 0.3.8的框架
实际上,Unity中的C#部分是不能进行热更新的,只有Lua与ui资源能够进行热更新。在Web服务器上面部署最新的版本资源文件,我们就可以将Lua的代码编码后上传到Web服务器上面去,当游戏客户端启动的时候,它会启动解包流程、解包的资源一般都是当时做包时候的资源,到后面我吗修改以后,它内部的资源可能已经不是最新的了,但是大部分可能是新的,只有少部分需要更新,那紧接着就启动更新流程,从Web服务器上面的资源配置列表里面,通过MD5/CRC比较查询到最新的资源,下载更新本地的文件,达到更新最新版的目的,游戏顺利启动。
具体的流程如下:
1.将资源打包到StreamingAssets中,因为Unity打包生成Apk/Ipa的时候,此目录会原样地打进安装包中,游戏客户端框架可以通过代码读取到里面的资源,并且把里面的资源复制到玩家的手机本地存储里面,这叫做解包。
那怎么打包呢?打包的资源分为素材资源与代码资源,这两部分的打包,框架都集成了,你可以直接修改里面的脚本逻辑适应自己的游戏项目,我们打开ulua/Editor/Packager.cs打包脚本。这里面根据不同的平台打包相应的资源。
[MenuItem("Game/Build iPhone Resource", false, 11)]
public static void BuildiPhoneResource() {
BuildTarget target;
#if UNITY_5
target = BuildTarget.iOS;
#else
target = BuildTarget.iPhone;
#endif
BuildAssetResource(target, false);
}
[MenuItem("Game/Build Android Resource", false, 12)]
public static void BuildAndroidResource() {
BuildAssetResource(BuildTarget.Android, true);
}
[MenuItem("Game/Build Windows Resource", false, 13)]
public static void BuildWindowsResource() {
BuildAssetResource(BuildTarget.StandaloneWindows, true);
}
重要的函数是下面这个,它打包了框架自带例子的素材文件后,继续处理Lua代码文件的打包。
public static void BuildAssetResource(BuildTarget target, bool isWin) {
if (AppConst.ExampleMode) {
HandleExampleBundle(target);
}
HandleLuaFile(isWin);
AssetDatabase.Refresh();
}
HandleExampleBundle函数就是将Examples/Builds下面的素材统一按照Unity的规则进行打包成assetbundle文件。没什么好说的。
我们主要是看HandleLuaFile函数的操作流程。
(1)在StreamingAssets目录下面新建lua目录,用于存放编码后的lua文件。
(2)遍历Lua目录下面所有的lua文件,并且根据目录结构创建相应目录树。
(3)如果AppConst.LuaEncode 设置了编码开关,就启动编码操作,否则直接复制源码过去。
(4)然后将前面的图片素材assetbundle跟相对应的lua文件目录,统一遍历计算出MD5/CRC,生成到files.txt里面。
这个代码就做了这几件事情,唯一需要展开介绍的只有怎么编码Lua文件了吧。这个函数其实并不长,
他就是根据相应的平台配置其编码器,编码器存放的路径也就是前面工程外的LuaEncoder的目录,拼凑出它的路径后,我们开始拼凑其编码的参数命令行,
需要注意的是luajit跟luavm的编码命令行不同:
luajit的:luajit.exe -b srcfile outfile
luavm的:luac -o outfile srcfile
命令行的格式其实都很简洁,然后我们启动C#的调用外部shell程序的代码,调用他们来编码出2进制文件即可。那最后统一计算MD5的文件就变成了编码的2进制的值。
//————————————————————————————————————
说完了打包流程,接下来说下怎么更新,只有能顺利更新下面后,才能够算作热更的最后一环。关于更新这一块,我们是用C#写成的,为啥?因为我们真实项目中,游戏的基础功能,比如下载、线程操作都建议在C#中完成,为了效率,而不是为了花架子都在lua中完成。因为这些基础功能,相对于游戏来说,更新的频率非常之低,用C#完成追求了效率,也没有任何损失。
我们打开Scripts/Manager/GameManager.cs文件,定位到IEnumerator OnUpdateResource()函数,
(1)它一上来就做些初始化的操作,找到更新的地址URL等。
(2)请求更新列表文件files.txt,因为里面存放了上面生成的目录结构及其MD5/CRC的信息。
(3)分析Web服务器上的files.txt文件内容,然后遍历检查本地的文件结构是否完整、MD5是否匹配。
(4)如果MD5不匹配,或者本地文件不存在,就开始创建一个下载请求,并且将它传递给线程,请求线程下载。
(5)本地协程会每一帧查询线程下载完后是否将下载的文件名存放到下载文件列表中,如果找到,继续下载下一个,直至全部下载完成。
其实当全部更新完成后,此时手机存储的文件结构及其内容已经是最新的了,客户端程序便可以顺利启动,完成了热更的最后一环。