zoukankan      html  css  js  c++  java
  • (转)Unity使用tolua框架教程: LuaFramewrk

    一、tolua下载

    toluaGitHub下载地址: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即可:

      1、打开CustomSettings.cs,把 _GT(typeof(Light)),和 _GT(typeof(QualitySettings)),这两行注释掉
      2、然后单击菜单【Lua】-【Clear wrap files】清理掉Wrap
      3、然后再单击菜单【Lua】-【Generate All】重新生成Wrap,
      4、然后再重新点击菜单【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。

    // LuaState.cs
    void OpenBaseLibs()
    {
        // ...
    
        BeginModul("UnityEngine");
        // ...
        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代码是这样的:

    public class UnityEngine_TransformWrap
    {
        public static void Register(LuaState L)
        {    
            L.BeginClass(typeof(UnityEngine.Transform), typeof(UnityEngine.Component));
        
            // ...
        }
    }

    当你在dropType数组中添加typeof(UnityEngine.Component),那么生成出来的UnityEngine_RendererWrap是这样的:

    public class UnityEngine_TransformWrap
    {
        public static void Register(LuaState L)
        {    
            L.BeginClass(typeof(UnityEngine.Transform), typeof(UnityEngine.Object));
        
            // ...
        }
    }

    发现没有,会认为Transform是继承Object,而事实上,Transform是继承Component的,这样会导致你在lua中对于Component子类的对象无法访问Component的public成员、属性和方法。
    比如下面这个会报错,提示不存在gameObject成员或属性。

    -- 假设r是Transform对象
    print(t.gameObject)

    解决办法就是不要在dropType数组中添加过滤类,而是在ToLuaExport.cs类的Generate方法中进行过滤,例:

    // ToLuaExport.cs
    public static void Generate(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)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,
    将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

    所有的controlerCtrlManager中注册

    -- CtrlManager.lua
    function CtrlManager.Init()
        logWarn("CtrlManager.Init----->>>");
        ctrlList[CtrlNames.Prompt] = PromptCtrl.New();
        ctrlList[CtrlNames.Message] = MessageCtrl.New();
        return this;
    end

    通过CtrlManager获取对应的controler对象,调用Awake()方法

    -- CtrlManager.lua
    local ctrl = CtrlManager.GetCtrl(CtrlNames.Prompt);
    if ctrl ~= nil then
        ctrl:Awake();
    end

    controler类中,Awake()方法中调用C#PanelManagerCreatePanel方法

    -- PromptCtrl.lua
    function PromptCtrl.Awake()
        logWarn("PromptCtrl.Awake--->>");
        panelMgr:CreatePanel('Prompt', this.OnCreate);
    end

    C#PanelManagerCreatePanel方法去加载界面预设,并挂上LuaBehaviour脚本

    这个LuaBehaviour脚本,主要是管理panel的生命周期,调用luapanelAwake,获取UI元素对象

    -- PromptPanel.lua
    
    local transform;
    local gameObject;
    
    PromptPanel = {};
    local this = PromptPanel;
    
    --启动事件--
    function PromptPanel.Awake(obj)
        gameObject = obj;
        transform = obj.transform;
    
        this.InitPanel();
        logWarn("Awake lua--->>"..gameObject.name);
    end
    
    --初始化面板--
    function PromptPanel.InitPanel()
        this.btnOpen = transform:Find("Open").gameObject;
        this.gridParent = transform:Find('ScrollView/Grid');
    end
    
    --单击事件--
    function PromptPanel.OnDestroy()
        logWarn("OnDestroy---->>>");
    end

    panelAwake执行完毕后,就会执行controlerOnCreate(),在controler中对UI元素对象添加一些事件和控制

    -- PromptCtrl.lua
    --启动事件--
    function PromptCtrl.OnCreate(obj)
        gameObject = obj;
        transform = obj.transform;
    
        panel = transform:GetComponent('UIPanel');
        prompt = transform:GetComponent('LuaBehaviour');
        logWarn("Start lua--->>"..gameObject.name);
    
        prompt:AddClick(PromptPanel.btnOpen, this.OnClick);
        resMgr:LoadPrefab('prompt', { 'PromptItem' }, this.InitPanel);
    end

    2、StartUp启动框架

    AppFacade.Instance.StartUp();   //启动游戏

    这个接口会抛出一个NotiConst.START_UP事件,对应的响应类是StartUpCommand

    using UnityEngine;
    using System.Collections;
    using LuaFramework;
    
    public class StartUpCommand : ControllerCommand {
    
        public override void Execute(IMessage message) {
            if (!Util.CheckEnvironment()) return;
    
            GameObject gameMgr = GameObject.Find("GlobalGenerator");
            if (gameMgr != null) {
                AppView appView = gameMgr.AddComponent<AppView>();
            }
            //-----------------关联命令-----------------------
            AppFacade.Instance.RegisterCommand(NotiConst.DISPATCH_MESSAGE, typeof(SocketCommand));
    
            //-----------------初始化管理器-----------------------
            AppFacade.Instance.AddManager<LuaManager>(ManagerName.Lua);
            AppFacade.Instance.AddManager<PanelManager>(ManagerName.Panel);
            AppFacade.Instance.AddManager<SoundManager>(ManagerName.Sound);
            AppFacade.Instance.AddManager<TimerManager>(ManagerName.Timer);
            AppFacade.Instance.AddManager<NetworkManager>(ManagerName.Network);
            AppFacade.Instance.AddManager<ResourceManager>(ManagerName.Resource);
            AppFacade.Instance.AddManager<ThreadManager>(ManagerName.Thread);
            AppFacade.Instance.AddManager<ObjectPoolManager>(ManagerName.ObjectPool);
            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才可以运行到。

    #if UNITY_EDITOR
            public const bool LuaBundleMode = false;                    //Lua代码AssetBundle模式
    #else
            public const bool LuaBundleMode = true;                    //Lua代码AssetBundle模式
    #endif

     

    5、Lua代码的读取

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

    /// LuaFramework.Util.DataPath
    
    /// <summary>
    /// 应用程序内容路径
    /// AppConst.AssetDir = "StreamingAssets"
    /// </summary>
    public static string AppContentPath() {
        string path = string.Empty;
        switch (Application.platform) {
            case RuntimePlatform.Android:
                path = "jar:file://" + Application.dataPath + "!/assets/";
            break;
            case RuntimePlatform.IPhonePlayer:
                path = Application.dataPath + "/Raw/";
            break;
            default:
                path = Application.dataPath + "/" + AppConst.AssetDir + "/";
            break;
        }
        return path;
    }
    
    /// <summary>
    /// 取得数据存放目录
    /// </summary>
    public static string 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中执行的),我们可以在GameManagerDoFile我们自定义的lua脚本,比如Game.lua脚本。

    7、C#中如何直接调用lua的某个方法

    GameManager可以获取到LuaManager对象,通过LuaManager.CallFunction接口调用。
    也可以用Util.CallMethod接口调用,两个接口的参数有差异,需要注意。

    /// LuaManager.CallFunction接口
    public object[] CallFunction(string funcName, params object[] args) {
          LuaFunction func = lua.GetFunction(funcName);
          if (func != null) {
              return func.LazyCall(args);
          }
          return null;
      }
    
    /// Util.CallMethod接口
    public static object[] CallMethod(string module, string func, params object[] args) {
        LuaManager luaMgr = AppFacade.Instance.GetManager<LuaManager>(ManagerName.Lua);
        if (luaMgr == null) return null;
        return luaMgr.CallFunction(module + "." + func, args);
    }

    8、lua中如何调用C#的方法

    假设现在我们有一个C#

    using UnityEngine;
    
    public class MyTest : MonoBehaviour
    {
        public int myNum;
    
        public void SayHello()
        {
            Debug.Log("Hello,I am MyTest,myNum: " + myNum);
        }
    
        public static void StaticFuncTest()
        {
            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才能运行)

    function Game.TestFunc()
        -- 静态方法访问
        MyTest.StaticFuncTest()
       
        local go = UnityEngine.GameObject("go")
        local myTest = go:AddComponent(typeof(MyTest))
        
        -- 成员变量
        myTest.myNum = 5
        -- 成员方法
        myTest:SayHello()
    end

    调用Game.TestFunc()

     注意,静态方法、静态变量、成员变量、成员属性使用 “.” 来访问,比如上面的 myTest.myNum,成员函数使用 “:” 来访问,比如上面的 myTest:SayHello()

    9、lua中如何使用协程

    function fib(n)
        local a, b = 0, 1
        while n > 0 do
            a, b = b, a + b
            n = n - 1
        end
    
        return a
    end
    
    function CoFunc()
        print('Coroutine started')    
        for i = 0, 10, 1 do
            print(fib(i))                    
            coroutine.wait(0.1)                     
        end 
        print("current frameCount: "..Time.frameCount)
        coroutine.step()
        print("yield frameCount: "..Time.frameCount)
    
        local www = UnityEngine.WWW("http://www.baidu.com")
        coroutine.www(www)
        local s = tolua.tolstring(www.bytes)
        print(s:sub(1, 128))
        print('Coroutine ended')
    end

    调用

    coroutine.start(CoFunc)

    如果要stop协程,则需要这样

    local co = coroutine.start(CoFunc)
    coroutine.stop(co)

    10、lua解析json

    假设现在有这么一份json文件

    {
        "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中要解析它

    local json = require 'cjson'
    
    function Test(str)
        local data = json.decode(str)
        print(data.glossary.title)
        s = json.encode(data)
        print(s)
    end

    调用Test(jsonStr)

    11、lua调用C#的托管

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

    12、lua通过反射调用C#

    有时候,我们没有把我们的C#类生成Wrap,但是又需要在lua中调用,这个时候,可以通过反射来调用。
    假设我们有一个C#类:MyClass

    // MyClass.cs
    public sealed class MyClass
    {
        //字段
        public string myName;
        //属性
        public int myAge { get; set; }
        
        //静态方法
        public static void SayHello()
        {
            Debug.Log("Hello, I am MyClass's static func: SayHello");
        }
    
        public void SayNum(int n)
        {
            Debug.Log("SayNum: " + n);
        }
    
        public void SayInfo()
        {
            Debug.Log("SayInfo, myName: " + myName + ",myAge: " + myAge);
        }
    }

    lua

    -- Game.lua
    function Game.TestReflection()
        require 'tolua.reflection'
        tolua.loadassembly('Assembly-CSharp')
        local BindingFlags = require 'System.Reflection.BindingFlags'
    
        local t = typeof('MyClass')
        -- 调用静态方法
        local func = tolua.getmethod(t, 'SayHello')
        func:Call()
        func:Destroy()
        func = nil
    
        -- 实例化
        local obj = tolua.createinstance(t)
        -- 字段
        local field = tolua.getfield(t, 'myName')
        -- 字段Set
        field:Set(obj, "linxinfa")
        -- 字段Get
        print('myName: ' .. field:Get(obj))
        field:Destroy()
        
        -- 属性
        local property = tolua.getproperty(t, 'myAge')
        -- 属性Set
        property:Set(obj, 29, null)
        -- 属性Get
        print('myAge: ' .. property:Get(obj, null))
        property:Destroy()
        
        --public成员方法SayNum
        func = tolua.getmethod(t, 'SayNum', typeof('System.Int32'))
        func:Call(obj, 666)
        func:Destroy()
        
        --public成员方法SayInfo
        func = tolua.getmethod(t, 'SayInfo')
        func:Call(obj)
        func:Destroy()
    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

    -- lua中对象判空
    function IsNilOrNull(o)
        return nil == o or null == o
    end

    14、获取今天是星期几

    -- 1是周日,2是周一,以此类推
    function GetTodayWeek()
        local t = os.date("*t", math.floor(os.time()))
        return t.wday
    end

    15、获取今天的年月日

    方法一

    function GetTodayYMD()
        local t = os.date("*t", math.floor(os.time()))
        return t.year .. "/" .. t.month .. "/" .. t.day
    end

    方法二

    function GetTodayYMD()
        -- 如果要显示时分秒,则用"%H:%M:%S"
        return os.date("%Y/%m%d", math.floor(os.time()))
    end

    16、字符串分割

    -- 参数str是你的字符串,比如"小明|小红|小刚"
    -- 参数sep是分隔符,比如"|"
    -- 返回值为{"小明","小红","小刚"}
    function SplitString(str, sep)
        local sep = sep or " "
        local result = {}
        local pattern = string.format("([^%s]+)", sep)
        string.gsub(s, pattern, function(c) result[#result + 1] = c end)
        return result 
    end

    17、大数字加逗号分割(数字会转成字符串)

    -- 参数num是数字,如3428439,转换结果"3,428,439"
    function FormatNumStrWithComma(num)
        local numstr = tostring(num)
        local strlen = string.len(numstr)
        local splitStrArr = {}
        for i = strlen, 1, -3 do
            local beginIndex = (i - 2 >= 1) and (i - 2) or 1
            table.insert(splitStrArr, string.sub(numstr, beginIndex, i))
        end
        local cnt = #splitStrArr
        local result = ""
        for i = cnt, 1, -1 do
            if i == cnt then
                result = result .. splitStrArr[i]
            else
                result = result .. "," .. splitStrArr[i]
            end
        end
        return result
    end

    18、通过组件名字添加组件

    -- 缓存
    local name2Type = {}
    -- 参数gameObject物体对象
    -- 参数componentName,组件名字,字符串
    function AddComponent(gameObject, componentName)
        local component = gameObject:GetComponent(componentName)
        if nil ~= component then return component end
    
        local componentType = name2Type[componentName]
        if nil == componentType then
            componentType  = System.Type.GetType(componentName)
            if nil == componentType then
                print("AddComponent Error: " .. componentName)
                return nil
            else
                name2Type[componentName] = componentType 
            end
        end
        return gameObject:AddComponent(componentType)
    end

    19、深拷贝

    lua中的table是引用类型,有时候我们为了不破坏原有的table,可能要用到深拷贝

    function DeepCopy(t)
        if nil == t then return nil end
        local result = ()
        for k, v in pairs(t) do
            if "table" == type(v) then
                result[k] = DeepCopy(v)
            else
                result[k] = v
            end
        end
        return result
    end

    20、四舍五入

    function Round(fnum)
        return math.floor(fnum + 0.5)
    end

    21、检测字符串是否含有中文

    -- 需要把C#的System.Text.RegularExpressions.Regex生成Wrap类
    function CheckIfStrContainChinese(str)
        return System.Text.RegularExpressions.Regex.IsMatch(str, "[\\u4e00-\\u9fa5]")
    end

    22、数字的位操作get、set

    -- 通过索引获取数字的某一位,index从1开始
    function GetBitByIndex(num, index)
        if nil == index then
            print("LuaUtil.GetBitByIndex Error, nil == index")
            return 0
        end
        local b = bit32.lshift(1,(index - 1))
        if nil == b then
            print("LuaUtil.GetBitByIndex Error, nil == b")
            return 0
        end
        return bit32.band(num, b)
    end
    
    -- 设置数字的某个位为某个值,num:目标数字,index:第几位,从1开始,v:要设置成的值,0或1
    function SetBitByIndex(num, index, v)
        local b = bit32.lshift(1,(index - 1))
        if v > 0 then
            num = bit32.bor(num, b)
        else
            b = bit32.bnot(b)
            num = bit32.band(num, b)
        end
        return num
    end

    23、限制字符长度,超过进行截断

    有时候,字符串过长需要截断显示,比如有一个昵称叫“我的名字特别长一行显示不下”,需求上限制最多显示5个字,超过的部分以…替代,即"我的名字特…"。首先要计算含有中文的字符串长度,然后再进行截断

    -- 含有中文的字符串长度
    function StrRealLen(str)
        if str == nil then return 0 end
        local count = 0
        local i = 1
        while (i < #str) do
            local curByte = string.byte(str, i)
            local byteCount = 1
            if curByte >= 0 and curByte <= 127 then
                byteCount = 1
            elseif curByte >= 192 and curByte <= 223 then
                byteCount = 2
            elseif curByte >= 224 and curByte <= 239 then
                byteCount = 3
            elseif curByte >= 240 and curByte <= 247 then
                byteCount = 4
            end
            local char = string.sub(str, i, i + byteCount - 1)
            i = i + byteCount
            count = count + 1
        end
        return count
    end
    
    -- 限制字符长度(多少个字)
    -- 参数str,为字符串
    -- 参数limit为限制的字数,如8
    -- 参数extra为当超过字数时,在尾部显示的字符串,比如"..."
    function LimitedStr(str, limit, extra)
        limit = limit or 8
        extra = extra or ""
        local text = ""
        -- 含有中文的字符串长度
        if StrRealLen(str) > limit then
            text = LuaUtil.sub_chars(str, limit) .. "..." .. extra
        else
            text = str .. extra
        end
        return text
    end

    24、判断字符串A是否已某个字符串B开头

    -- 判断字符串str是否是以某个字符串start开头
    function StringStartsWith(str, start)
        return string.sub(str, 1, string.len(start)) == start
    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、真机热更资源存放路径

    persistentDataPath/res/
                          ├──/update/
                          │       ├──/lua/   
                          │       │    └──lua_update.bundle            #lua增量bundle
                          │       ├──/res/
                          │       │    ├──aaa.bundle                   #预设aaa的bundle
                          │       │    ├──bbb.bundle                   #音频bbb的bundle
                          │       │    └──...                          #其他各种格式的资源bundle
                          │       └──/cfg/
                          │            ├──cfg.bundle                   #配置增量bundle
                          │            └──...                          #其他文本或二进制文件增量bundle
                          ├──out_put.log                               #游戏日志
                          └──...

    关于persistentDataPath,可以参见我这篇博客:https://blog.csdn.net/linxinfa/article/details/51679528

    转载链接:https://blog.csdn.net/linxinfa/article/details/88246345

  • 相关阅读:
    HDU 6071
    HDU 6073
    HDU 2124 Repair the Wall(贪心)
    HDU 2037 今年暑假不AC(贪心)
    HDU 1257 最少拦截系统(贪心)
    HDU 1789 Doing Homework again(贪心)
    HDU 1009 FatMouse' Trade(贪心)
    HDU 2216 Game III(BFS)
    HDU 1509 Windows Message Queue(队列)
    HDU 1081 To The Max(动态规划)
  • 原文地址:https://www.cnblogs.com/wodehao0808/p/15749101.html
Copyright © 2011-2022 走看看