找回密码
 立即注册
查看: 457|回复: 0

Unity使用tolua框架教程: LuaFramewrk

[复制链接]
发表于 2021-8-12 17:50 | 显示全部楼层 |阅读模式
文章目录



        一、tolua下载二、运行Demo
          1、生成注册文件2、将lua打成AssetBundle3、解决报错4、为何一些没有在CustomSettings.cs注册的类也会生成Wrap类5、顺利生成AssetBundle6、运行Demo场景7、Unity2020无报错版LuaFramework-UGUI
        三、开发环境IDE四、接口讲解
          1、MVC框架2、StartUp启动框架3、LuaManager核心管理器4、AppConst常量定义5、Lua代码的读取6、GameManager游戏管理器7、C#中如何直接调用lua的某个方法8、lua中如何调用C#的方法9、lua中如何使用协程10、lua解析json11、lua调用C#的托管12、lua通过反射调用C#13、nil和null14、获取今天是星期几15、获取今天的年月日16、字符串分割17、大数字加逗号分割(数字会转成字符串)18、通过组件名字添加组件19、深拷贝20、四舍五入21、检测字符串是否含有中文22、数字的位操作get、set23、限制字符长度,超过进行截断24、判断字符串A是否已某个字符串B开头
        五、热更lua与资源
          1、热更lua2、热更资源热更资源3、真机热更资源存放路径




一、tolua下载

tolua的GitHub下载地址:https://github.com/topameng/tolua



假设我们下载的是LuaFramework_UGUI,它是基于Unity 5.0 + UGUI + tolua构建的工程



下载下来得到一个LuaFramework_UGUI-master.zip



二、运行Demo

1、生成注册文件

解压之后就是一个Unity的工程,直接用Unity打开,首次打开工程会询问生成注册文件,点击确定即可



2、将lua打成AssetBundle

首先要执行lua资源的生成(打AssetBundle),点击菜单【LuaFramework】-【Build Windows Resource】


会把lua代码打成AssetBundle放在StreamingAssets中。
3、解决报错

如果你用的不是Unity5.x,而是Unity2020,那么可能会报错:


这是因为新版本的Unity有些属性和接口已经废弃了的原因,我们需要特殊处理一下
一个是Light类,一个是QualitySettings类,这两个类我们一般不需要在lua中使用,所以我们不对他们生产Wrap即可:
    打开CustomSettings.cs,把 _GT(typeof(Light)),和 _GT(typeof(QualitySettings)),这两行注释掉然后单击菜单【Lua】-【Clear wrap files】清理掉Wrap然后再单击菜单【Lua】-【Generate All】重新生成Wrap,然后再重新点击菜单【LuaFramework】-【Build Windows Resource】生成lua资源。
执行【Lua】-【Generate All】菜单的时候,你可能会报错


定位到报错的位置


添加判空


重新执行【Lua】-【Generate All】菜单
生成后应该还有报错


这是因为新版的ParticleSystem类新增了一些接口,我们可以定位到对应报错的地方,把报错的地方注释掉。
不过为了防止下次执行【Lua】-【Generate All】菜单时又被覆盖导致报错,我们可以把UnityEngine_ParticleSystemWrap.cs移动到BaseType目录中


并把CustomSettings.cs中的_GT(typeof(ParticleSystem)),注释掉。
并在LuaState.cs注册ParticleSystemWrap类,要注意调用点要放在对应的BeginModul和EndModule之间,是什么命名空间下的,就放在什么Modul之下,如果是多级命名空间,则是嵌套多个BeginModul和EndModule。
  1. // LuaState.csvoidOpenBaseLibs(){// ...BeginModul("UnityEngine");// ...
  2.         UnityEngine_ParticleSystemWrap.Register(this);EndModule();//end UnityEngine}
复制代码
同理,UnityEngine_MeshRendererWrap.cs可能也会报错,按上面的处理方式处理。
4、为何一些没有在CustomSettings.cs注册的类也会生成Wrap类

假设我们把某个Wrap类手动移动到BaseType目录中,并在CustomSettings.cs中注释掉对应的_GT(typeof(xxx)),理论上应该不会生成对应的Wrap类,但事实上可能还是生成了,为什么?
这是因为ToLua会将在CustomSettings.cs中注册的类的父类进行递归生成。
举个例子,CustomSettings.cs中把_GT(typeof(Component))注释掉,执行【Lua】-【Generate All】菜单,依然会生成UnityEngine_ComponentWrap.cs,为什么?
因为在CustomSettings.cs中有_GT(typeof(Transform)),而Transform的父类是Component,所以依然会生成UnityEngine_ComponentWrap.cs。
具体逻辑可以看ToLuaMenu.cs的AutoAddBaseType函数,它里面就是进行递归生成父类的Wrap类的。
如果你将UnityEngine_ComponentWrap.cs移动到BaseType目录中,并且不想重新生成UnityEngine_ComponentWrap.cs,可以在ToLuaMenu.cs的dropType数组中添加typeof(UnityEngine.Component)即可,不过不建议这么做,因为这里有个坑!
这个坑就是Component的子类生成Wrap类是错误的。举个例子,Transform是继承Component,生成的UnityEngine_TransformWrap代码是这样的:
  1. publicclassUnityEngine_TransformWrap{publicstaticvoidRegister(LuaState L){       
  2.                 L.BeginClass(typeof(UnityEngine.Transform),typeof(UnityEngine.Component));// ...}}
复制代码
当你在dropType数组中添加typeof(UnityEngine.Component),那么生成出来的UnityEngine_RendererWrap是这样的:
  1. publicclassUnityEngine_TransformWrap{publicstaticvoidRegister(LuaState L){       
  2.                 L.BeginClass(typeof(UnityEngine.Transform),typeof(UnityEngine.Object));// ...}}
复制代码
发现没有,会认为Transform是继承Object,而事实上,Transform是继承Component的,这样会导致你在lua中对于Component子类的对象无法访问Component的public成员、属性和方法。
比如下面这个会报错,提示不存在gameObject成员或属性。
  1. -- 假设r是Transform对象print(t.gameObject)
复制代码
解决办法就是不要在dropType数组中添加过滤类,而是在ToLuaExport.cs类的Generate方法中进行过滤,例:
  1. // ToLuaExport.cspublicstaticvoidGenerate(string dir){// ...if(type(Component)== type){return;}// ...}
复制代码
5、顺利生成AssetBundle

最后,【LuaFramework】-【Build Windows Resource】成功生成AssetBundle,我们可以在StreamingAssets中看到很多AssetBundle文件。



6、运行Demo场景

接下来,我们就可以运行Demo场景了。打开main场景


运行效果



7、Unity2020无报错版LuaFramework-UGUI

如果你不想手动修复上报的报错,我将修复好的版本上传到了GitHub,使用Unity2020可以直接运行。
GitHub工程地址:https://github.com/linxinfa/Unity2020-LuaFramework-UGUI



三、开发环境IDE

可以使用subline,也可以使用visual studio,个人偏好使用visual studio,配合插件BabeLua
Unity写lua代码的vs插件:BabeLua: https://blog.csdn.net/linxinfa/article/details/88191485
四、接口讲解

1、MVC框架


上面这个Lua动态创建出来的面板的控制逻辑在PromptCtrl.lua脚本中,我们可以看到lua工程中使用了经典的MVC框架。



MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
所有的controler在CtrlManager中注册
  1. -- CtrlManager.lua
  2. function CtrlManager.Init()logWarn("CtrlManager.Init----->>>");
  3.         ctrlList[CtrlNames.Prompt]= PromptCtrl.New();
  4.         ctrlList[CtrlNames.Message]= MessageCtrl.New();return this;
  5. end
复制代码
通过CtrlManager获取对应的controler对象,调用Awake()方法
  1. -- CtrlManager.lua
  2. local ctrl = CtrlManager.GetCtrl(CtrlNames.Prompt);if ctrl ~= nil then
  3.     ctrl:Awake();
  4. end
复制代码
controler类中,Awake()方法中调用C#的PanelManager的CreatePanel方法
  1. -- PromptCtrl.lua
  2. function PromptCtrl.Awake()logWarn("PromptCtrl.Awake--->>");
  3.         panelMgr:CreatePanel('Prompt', this.OnCreate);
  4. end
复制代码
C#的PanelManager的CreatePanel方法去加载界面预设,并挂上LuaBehaviour脚本


这个LuaBehaviour脚本,主要是管理panel的生命周期,调用lua中panel的Awake,获取UI元素对象
  1. -- PromptPanel.lua
  2. local transform;
  3. local gameObject;
  4. PromptPanel ={};
  5. local this = PromptPanel;--启动事件--
  6. function PromptPanel.Awake(obj)
  7.         gameObject = obj;
  8.         transform = obj.transform;
  9.         this.InitPanel();logWarn("Awake lua--->>"..gameObject.name);
  10. end
  11. --初始化面板--
  12. function PromptPanel.InitPanel()
  13.         this.btnOpen = transform:Find("Open").gameObject;
  14.         this.gridParent = transform:Find('ScrollView/Grid');
  15. end
  16. --单击事件--
  17. function PromptPanel.OnDestroy()logWarn("OnDestroy---->>>");
  18. end
复制代码
panel的Awake执行完毕后,就会执行controler的OnCreate(),在controler中对UI元素对象添加一些事件和控制
  1. -- PromptCtrl.lua
  2. --启动事件--
  3. function PromptCtrl.OnCreate(obj)
  4.         gameObject = obj;
  5.         transform = obj.transform;
  6.         panel = transform:GetComponent('UIPanel');
  7.         prompt = transform:GetComponent('LuaBehaviour');logWarn("Start lua--->>"..gameObject.name);
  8.         prompt:AddClick(PromptPanel.btnOpen, this.OnClick);
  9.         resMgr:LoadPrefab('prompt',{'PromptItem'}, this.InitPanel);
  10. end
复制代码
2、StartUp启动框架
  1. AppFacade.Instance.StartUp();//启动游戏
复制代码
这个接口会抛出一个NotiConst.START_UP事件,对应的响应类是StartUpCommand
  1. using UnityEngine;using System.Collections;using LuaFramework;publicclassStartUpCommand:ControllerCommand{publicoverridevoidExecute(IMessage message){if(!Util.CheckEnvironment())return;GameObject gameMgr = GameObject.Find("GlobalGenerator");if(gameMgr !=null){AppView appView = gameMgr.AddComponent<AppView>();}//-----------------关联命令-----------------------
  2.         AppFacade.Instance.RegisterCommand(NotiConst.DISPATCH_MESSAGE,typeof(SocketCommand));//-----------------初始化管理器-----------------------
  3.         AppFacade.Instance.AddManager<LuaManager>(ManagerName.Lua);
  4.         AppFacade.Instance.AddManager<PanelManager>(ManagerName.Panel);
  5.         AppFacade.Instance.AddManager<SoundManager>(ManagerName.Sound);
  6.         AppFacade.Instance.AddManager<TimerManager>(ManagerName.Timer);
  7.         AppFacade.Instance.AddManager<NetworkManager>(ManagerName.Network);
  8.         AppFacade.Instance.AddManager<ResourceManager>(ManagerName.Resource);
  9.         AppFacade.Instance.AddManager<ThreadManager>(ManagerName.Thread);
  10.         AppFacade.Instance.AddManager<ObjectPoolManager>(ManagerName.ObjectPool);
  11.         AppFacade.Instance.AddManager<GameManager>(ManagerName.Game);}}
复制代码
这里初始化了各种管理器,我们可以根据具体需求进行改造和自定义。
3、LuaManager核心管理器

LuaManager这个管理器是必须的,掌管整个lua虚拟机的生命周期。它主要是加载lua库,加载lua脚本,启动lua虚拟机,执行Main.lua。
4、AppConst常量定义

AppConst定义了一些常量。
其中AppConst.LuaBundleMode是lua代码AssetBundle模式。它会被赋值给LuaLoader的beZip变量,在加载lua代码的时候,会根据beZip的值去读取lua文件,false则去search path中读取lua文件,否则从外部设置过来的bundle文件中读取lua文件。默认为true。在Editor环境下,建议把AppConst.LuaBundleMode设为false,这样方便运行,否则写完lua代码需要生成AssetBundle才可以运行到。
  1. #if UNITY_EDITORpublicconstbool LuaBundleMode =false;//Lua代码AssetBundle模式#elsepublicconstbool LuaBundleMode =true;//Lua代码AssetBundle模式#endif
复制代码
5、Lua代码的读取

LuaLoader和LuaResLoader都继承LuaFileUtils。lua代码会先从LuaFramework.Util.AppContentPath目录解压到LuaFramework.Util.DataPath目录中,lua文件列表信息记录在files.txt中,此文件也会拷贝过去。然后从LuaFramework.Util.DataPath目录中读取lua代码。


  1. /// LuaFramework.Util.DataPath/// <summary>/// 应用程序内容路径/// AppConst.AssetDir = "StreamingAssets"/// </summary>publicstaticstringAppContentPath(){string path =string.Empty;switch(Application.platform){case RuntimePlatform.Android:
  2.             path ="jar:file://"+ Application.dataPath +"!/assets/";break;case RuntimePlatform.IPhonePlayer:
  3.             path = Application.dataPath +"/Raw/";break;default:
  4.             path = Application.dataPath +"/"+ AppConst.AssetDir +"/";break;}return path;}/// <summary>/// 取得数据存放目录/// </summary>publicstaticstring DataPath {get{string game = AppConst.AppName.ToLower();if(Application.isMobilePlatform){return Application.persistentDataPath +"/"+ game +"/";}if(AppConst.DebugMode){return Application.dataPath +"/"+ AppConst.AssetDir +"/";}if(Application.platform == RuntimePlatform.OSXEditor){int i = Application.dataPath.LastIndexOf('/');return Application.dataPath.Substring(0, i +1)+ game +"/";}return"c:/"+ game +"/";}}
复制代码
完了之后,再进行远程的更新检测,看看用不用热更lua代码,远程url就是AppConst.WebUrl,先下载files.txt,然后再读取lua文件列表进行下载。
6、GameManager游戏管理器

启动框架后,会创建GameManager游戏管理器,它负责检测lua逻辑代码的更新检测和加载(Main.lua是在LuaManager中执行的),我们可以在GameManager中DoFile我们自定义的lua脚本,比如Game.lua脚本。
7、C#中如何直接调用lua的某个方法

GameManager可以获取到LuaManager对象,通过LuaManager.CallFunction接口调用。
也可以用Util.CallMethod接口调用,两个接口的参数有差异,需要注意。
  1. /// LuaManager.CallFunction接口publicobject[]CallFunction(string funcName,paramsobject[] args){LuaFunction func = lua.GetFunction(funcName);if(func !=null){return func.LazyCall(args);}returnnull;}/// Util.CallMethod接口publicstaticobject[]CallMethod(string module,string func,paramsobject[] args){LuaManager luaMgr = AppFacade.Instance.GetManager<LuaManager>(ManagerName.Lua);if(luaMgr ==null)returnnull;return luaMgr.CallFunction(module +"."+ func, args);}
复制代码
8、lua中如何调用C#的方法

假设现在我们有一个C#类
  1. using UnityEngine;publicclassMyTest:MonoBehaviour{publicint myNum;publicvoidSayHello(){
  2.         Debug.Log("Hello,I am MyTest,myNum: "+ myNum);}publicstaticvoidStaticFuncTest(){
  3.         Debug.Log("I am StaticFuncTest");}}
复制代码
我们想在lua中访问这个MyTest类的函数。首先,我们需要在CustomSettings.cs中的customTypeList数组中添加类的注册:
_GT(typeof(MyTest)),
然后然后再单击菜单【Lua】-【Generate All】生成Wrap,生成完我们会看到一个MyTestWrap类


接下来就可以在lua中访问了。(注意AppConst.LuaBundleMode的值要设为false,方便Editor环境下运行lua代码,否则需要先生成AssetBundle才能运行)
  1. function Game.TestFunc()-- 静态方法访问
  2.     MyTest.StaticFuncTest()
  3.    
  4.     local go = UnityEngine.GameObject("go")
  5.     local myTest = go:AddComponent(typeof(MyTest))-- 成员变量
  6.     myTest.myNum =5-- 成员方法
  7.     myTest:SayHello()
  8. end
复制代码
调用Game.TestFunc()


注意,静态方法、静态变量、成员变量、成员属性使用 “.” 来访问,比如上面的 myTest.myNum,成员函数使用 “:” 来访问,比如上面的 myTest:SayHello()
9、lua中如何使用协程
  1. function fib(n)
  2.     local a, b =0,1while n >0do
  3.         a, b = b, a + b
  4.         n = n -1
  5.     end
  6.     return a
  7. end
  8. function CoFunc()print('Coroutine started')for i =0,10,1doprint(fib(i))                    
  9.         coroutine.wait(0.1)                     
  10.     end
  11.     print("current frameCount: "..Time.frameCount)
  12.     coroutine.step()print("yield frameCount: "..Time.frameCount)
  13.     local www = UnityEngine.WWW("http://www.baidu.com")
  14.     coroutine.www(www)
  15.     local s = tolua.tolstring(www.bytes)print(s:sub(1,128))print('Coroutine ended')
  16. end
复制代码
调用
  1. coroutine.start(CoFunc)
复制代码

如果要stop协程,则需要这样
  1. local co = coroutine.start(CoFunc)
  2. coroutine.stop(co)
复制代码
10、lua解析json

假设现在有这么一份json文件
  1. {"glossary":{"title":"example glossary","GlossDiv":{"title":"S","GlossList":{"GlossEntry":{"ID":"SGML","SortAs":"SGML","GlossTerm":"Standard Generalized Mark up Language","Acronym":"SGML","Abbrev":"ISO 8879:1986","GlossDef":{"para":"A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso":["GML","XML"]},"GlossSee":"markup"}}}}}
复制代码
假设我们已经把上面的json文件的内容保存到变量jsonStr字符串中,现在在lua中要解析它
  1. local json = require 'cjson'
  2. function Test(str)
  3.     local data = json.decode(str)print(data.glossary.title)
  4.     s = json.encode(data)print(s)
  5. end
复制代码
调用Test(jsonStr)



11、lua调用C#的托管
  1. // c#传托管给lua
  2. System.Action<string> cb =(s)=>{ Debug.Log(s);};
  3. Util.CallMethod("Game","TestCallBackFunc", cb);
复制代码
  1. -- lua调用C#的托管
  2. function Game.TestCallBackFunc(cb)if nil ~= cb then
  3.        System.Delegate.DynamicInvoke(cb,"Hello, I am lua, I call Delegate")
  4.     end
  5. end
复制代码


12、lua通过反射调用C#

有时候,我们没有把我们的C#类生成Wrap,但是又需要在lua中调用,这个时候,可以通过反射来调用。
假设我们有一个C#类:MyClass
  1. // MyClass.cspublicsealedclassMyClass{//字段publicstring myName;//属性publicint myAge {get;set;}//静态方法publicstaticvoidSayHello(){
  2.         Debug.Log("Hello, I am MyClass's static func: SayHello");}publicvoidSayNum(int n){
  3.         Debug.Log("SayNum: "+ n);}publicvoidSayInfo(){
  4.         Debug.Log("SayInfo, myName: "+ myName +",myAge: "+ myAge);}}
复制代码
在lua中
  1. -- Game.lua
  2. function Game.TestReflection()
  3.     require 'tolua.reflection'
  4.     tolua.loadassembly('Assembly-CSharp')
  5.     local BindingFlags = require 'System.Reflection.BindingFlags'
  6.     local t =typeof('MyClass')-- 调用静态方法
  7.     local func = tolua.getmethod(t,'SayHello')
  8.     func:Call()
  9.     func:Destroy()
  10.     func = nil
  11.     -- 实例化
  12.     local obj = tolua.createinstance(t)-- 字段
  13.     local field = tolua.getfield(t,'myName')-- 字段Set
  14.     field:Set(obj,"linxinfa")-- 字段Get
  15.     print('myName: '.. field:Get(obj))
  16.         field:Destroy()-- 属性
  17.     local property = tolua.getproperty(t,'myAge')-- 属性Set
  18.     property:Set(obj,29, null)-- 属性Get
  19.     print('myAge: '.. property:Get(obj, null))
  20.     property:Destroy()--public成员方法SayNum
  21.     func = tolua.getmethod(t,'SayNum',typeof('System.Int32'))
  22.     func:Call(obj,666)
  23.         func:Destroy()--public成员方法SayInfo
  24.     func = tolua.getmethod(t,'SayInfo')
  25.     func:Call(obj)
  26.     func:Destroy()
  27. end
复制代码
调用Game.TestReflection()



13、nil和null

nil是lua对象的空,null表示c#对象的空。假设我们在c#中有一个GameObject对象传递给了lua的对象a,接下来我们把这个GameObject对象Destroy了,并在c#中把这个GameObject对象赋值为null,此时lua中的对象a并不会等于nil
如果要在lua中判断一个对象是否为空,安全的做法是同时判断nil和null
  1. -- lua中对象判空
  2. function IsNilOrNull(o)return nil == o or null == o
  3. end
复制代码
14、获取今天是星期几
  1. --1是周日,2是周一,以此类推
  2. function GetTodayWeek()
  3.         local t = os.date("*t", math.floor(os.time()))return t.wday
  4. end
复制代码
15、获取今天的年月日

方法一
  1. function GetTodayYMD()
  2.         local t = os.date("*t", math.floor(os.time()))return t.year .."/".. t.month .."/".. t.day
  3. end
复制代码
方法二
  1. function GetTodayYMD()-- 如果要显示时分秒,则用"%H:%M:%S"return os.date("%Y/%m%d", math.floor(os.time()))
  2. end
复制代码
16、字符串分割
  1. -- 参数str是你的字符串,比如"小明|小红|小刚"-- 参数sep是分隔符,比如"|"-- 返回值为{"小明","小红","小刚"}
  2. function SplitString(str, sep)
  3.         local sep = sep or " "
  4.         local result ={}
  5.         local pattern = string.format("([^%s]+)", sep)
  6.         string.gsub(s, pattern,function(c) result[#result +1]= c end)return result
  7. end
复制代码
17、大数字加逗号分割(数字会转成字符串)
  1. -- 参数num是数字,如3428439,转换结果"3,428,439"
  2. function FormatNumStrWithComma(num)
  3.         local numstr =tostring(num)
  4.         local strlen = string.len(numstr)
  5.         local splitStrArr ={}for i = strlen,1,-3do
  6.                 local beginIndex =(i -2>=1) and (i -2) or 1
  7.                 table.insert(splitStrArr, string.sub(numstr, beginIndex, i))
  8.         end
  9.         local cnt = #splitStrArr
  10.         local result =""for i = cnt,1,-1doif i == cnt then
  11.                         result = result .. splitStrArr[i]else
  12.                         result = result ..",".. splitStrArr[i]
  13.                 end
  14.         end
  15.         return result
  16. end
复制代码
18、通过组件名字添加组件
  1. -- 缓存
  2. local name2Type ={}-- 参数gameObject物体对象
  3. -- 参数componentName,组件名字,字符串
  4. function AddComponent(gameObject, componentName)
  5.         local component = gameObject:GetComponent(componentName)if nil ~= component then return component end
  6.         local componentType = name2Type[componentName]if nil == componentType then
  7.                 componentType  = System.Type.GetType(componentName)if nil == componentType then
  8.                         print("AddComponent Error: ".. componentName)return nil
  9.                 else
  10.                         name2Type[componentName]= componentType
  11.                 end
  12.         end
  13.         return gameObject:AddComponent(componentType)
  14. end
复制代码
19、深拷贝

lua中的table是引用类型,有时候我们为了不破坏原有的table,可能要用到深拷贝
  1. function DeepCopy(t)if nil == t then return nil end
  2.         local result =()for k, v in pairs(t)doif"table"==type(v) then
  3.                         result[k]=DeepCopy(v)else
  4.                         result[k]= v
  5.                 end
  6.         end
  7.         return result
  8. end
复制代码
20、四舍五入
  1. function Round(fnum)return math.floor(fnum +0.5)
  2. end
复制代码
21、检测字符串是否含有中文
  1. -- 需要把C#的System.Text.RegularExpressions.Regex生成Wrap类
  2. function CheckIfStrContainChinese(str)return System.Text.RegularExpressions.Regex.IsMatch(str,"[\\u4e00-\\u9fa5]")
  3. end
复制代码
22、数字的位操作get、set
  1. -- 通过索引获取数字的某一位,index从1开始
  2. function GetBitByIndex(num, index)if nil == index then
  3.         print("LuaUtil.GetBitByIndex Error, nil == index")return0
  4.     end
  5.     local b = bit32.lshift(1,(index -1))if nil == b then
  6.         print("LuaUtil.GetBitByIndex Error, nil == b")return0
  7.     end
  8.     return bit32.band(num, b)
  9. end
  10. -- 设置数字的某个位为某个值,num:目标数字,index:第几位,从1开始,v:要设置成的值,0或1
  11. function SetBitByIndex(num, index, v)
  12.     local b = bit32.lshift(1,(index -1))if v >0 then
  13.         num = bit32.bor(num, b)else
  14.         b = bit32.bnot(b)
  15.         num = bit32.band(num, b)
  16.     end
  17.     return num
  18. end
复制代码
23、限制字符长度,超过进行截断

有时候,字符串过长需要截断显示,比如有一个昵称叫“我的名字特别长一行显示不下”,需求上限制最多显示5个字,超过的部分以…替代,即"我的名字特…"。首先要计算含有中文的字符串长度,然后再进行截断
  1. -- 含有中文的字符串长度
  2. function StrRealLen(str)if str == nil then return0 end
  3.     local count =0
  4.     local i =1while(i < #str)do
  5.         local curByte = string.byte(str, i)
  6.         local byteCount =1if curByte >=0 and curByte <=127 then
  7.             byteCount =1
  8.         elseif curByte >=192 and curByte <=223 then
  9.             byteCount =2
  10.         elseif curByte >=224 and curByte <=239 then
  11.             byteCount =3
  12.         elseif curByte >=240 and curByte <=247 then
  13.             byteCount =4
  14.         end
  15.         local char= string.sub(str, i, i + byteCount -1)
  16.         i = i + byteCount
  17.         count = count +1
  18.     end
  19.     return count
  20. end
  21. -- 限制字符长度(多少个字)
  22. -- 参数str,为字符串
  23. -- 参数limit为限制的字数,如8-- 参数extra为当超过字数时,在尾部显示的字符串,比如"..."
  24. function LimitedStr(str, limit, extra)
  25.     limit = limit or 8
  26.     extra = extra or ""
  27.     local text =""-- 含有中文的字符串长度
  28.     ifStrRealLen(str)> limit then
  29.         text = LuaUtil.sub_chars(str, limit).."...".. extra
  30.     else
  31.         text = str .. extra
  32.     end
  33.     return text
  34. end
复制代码
24、判断字符串A是否已某个字符串B开头
  1. -- 判断字符串str是否是以某个字符串start开头
  2. function StringStartsWith(str, start)return string.sub(str,1, string.len(start))== start
  3. end
复制代码
五、热更lua与资源

1、热更lua

打app整包的时候,备份一份lua全量文件,后面打lua增量包的时候,根据文件差异进行比对,新增和差异的lua文件打成一个lua_update.bundle,放在一个update文件夹中,并压缩成zip,放到服务器端,客户端通过https下载增量包并解压到Application.persistentDataPath目录。游戏加载lua文件的时候,优先从update文件夹中的lua_update.bundle中查找lua脚本。
2、热更资源热更资源

做个编辑器工具,指定某个或某些资源文件(预设、音频、动画、材质等),打成多个assetbundle,放在一个update文件夹中,并压缩成一个zip,放到服务器端,客户端通过https下载增量包并解压到Application.persistentDataPath目录。
游戏加载资源文件的时候,优先从update文件夹中查找对应的资源文件。
3、真机热更资源存放路径
  1. persistentDataPath/res/
  2.                       ├──/update/
  3.                       │       ├──/lua/   
  4.                       │       │    └──lua_update.bundle            #lua增量bundle
  5.                       │       ├──/res/
  6.                       │       │    ├──aaa.bundle                   #预设aaa的bundle
  7.                       │       │    ├──bbb.bundle                   #音频bbb的bundle
  8.                       │       │    └──...                          #其他各种格式的资源bundle
  9.                       │       └──/cfg/
  10.                       │            ├──cfg.bundle                   #配置增量bundle
  11.                       │            └──...                          #其他文本或二进制文件增量bundle
  12.                       ├──out_put.log                               #游戏日志
  13.                       └──...
复制代码
关于persistentDataPath,可以参见我这篇博客:https://blog.csdn.net/linxinfa/article/details/51679528

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-24 09:29 , Processed in 0.109495 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表