文章目录

  • 一、tolua下载
  • 二、运行Demo
    • 1、生成注册文件
    • 2、将lua打成AssetBundle
    • 3、解决报错
    • 4、为何一些没有在CustomSettings.cs注册的类也会生成Wrap类
    • 5、顺利生成AssetBundle
    • 6、运行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解析json
    • 11、lua调用C#的托管
    • 12、lua通过反射调用C#
    • 13、nil和null
    • 14、获取今天是星期几
    • 15、获取今天的年月日
    • 16、字符串分割
    • 17、大数字加逗号分割(数字会转成字符串)
    • 18、通过组件名字添加组件
    • 19、深拷贝
    • 20、四舍五入
    • 21、检测字符串是否含有中文
    • 22、数字的位操作get、set
    • 23、限制字符长度,超过进行截断
    • 24、判断字符串A是否已某个字符串B开头
  • 五、热更lua与资源
    • 1、热更lua
    • 2、热更资源热更资源
    • 3、真机热更资源存放路径

一、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类,要注意调用点要放在对应的BeginModulEndModule之间,是什么命名空间下的,就放在什么Modul之下,如果是多级命名空间,则是嵌套多个BeginModulEndModule

// 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.csAutoAddBaseType函数,它里面就是进行递归生成父类的Wrap类的。
如果你将UnityEngine_ComponentWrap.cs移动到BaseType目录中,并且不想重新生成UnityEngine_ComponentWrap.cs,可以在ToLuaMenu.csdropType数组中添加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子类的对象无法访问Componentpublic成员、属性和方法。
比如下面这个会报错,提示不存在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 thenctrl: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.lualocal 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_EDITORpublic const bool LuaBundleMode = false;                    //Lua代码AssetBundle模式
#elsepublic 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, 1while n > 0 doa, b = b, a + bn = n - 1endreturn a
endfunction CoFunc()print('Coroutine started')    for i = 0, 10, 1 doprint(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 thenSystem.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')-- 字段Setfield:Set(obj, "linxinfa")-- 字段Getprint('myName: ' .. field:Get(obj))field:Destroy()-- 属性local property = tolua.getproperty(t, 'myAge')-- 属性Setproperty:Set(obj, 29, null)-- 属性Getprint('myAge: ' .. property:Get(obj, null))property:Destroy()--public成员方法SayNumfunc = tolua.getmethod(t, 'SayNum', typeof('System.Int32'))func:Call(obj, 666)func:Destroy()--public成员方法SayInfofunc = tolua.getmethod(t, 'SayInfo')func:Call(obj)func:Destroy()
end

调用Game.TestReflection()

13、nil和null

nillua对象的空,null表示c#对象的空。假设我们在c#中有一个GameObject对象传递给了lua的对象a,接下来我们把这个GameObject对象Destroy了,并在c#中把这个GameObject对象赋值为null,此时lua中的对象a并不会等于nil
如果要在lua中判断一个对象是否为空,安全的做法是同时判断nilnull

-- 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 dolocal beginIndex = (i - 2 >= 1) and (i - 2) or 1table.insert(splitStrArr, string.sub(numstr, beginIndex, i))endlocal cnt = #splitStrArrlocal result = ""for i = cnt, 1, -1 doif i == cnt thenresult = result .. splitStrArr[i]elseresult = result .. "," .. splitStrArr[i]endendreturn result
end

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

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

19、深拷贝

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

function DeepCopy(t)if nil == t then return nil endlocal result = ()for k, v in pairs(t) doif "table" == type(v) thenresult[k] = DeepCopy(v)elseresult[k] = vendendreturn 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 thenprint("LuaUtil.GetBitByIndex Error, nil == index")return 0endlocal b = bit32.lshift(1,(index - 1))if nil == b thenprint("LuaUtil.GetBitByIndex Error, nil == b")return 0endreturn 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 thennum = bit32.bor(num, b)elseb = bit32.bnot(b)num = bit32.band(num, b)endreturn num
end

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

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

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

Unity使用tolua框架教程: LuaFramewrk相关推荐

  1. Unity 之 ToLua框架中UI.Dropdown组件动态添加Options

    前言 最近使用ToLua框架使用Unity中的Dropdown组件,其他属性都还好和在C#中使用的形式差不多,只是修改成了Lua的语法习惯就可以了,但是我有个需求,是要需要实现动态添加Dropdown ...

  2. Unity热更新ToLua框架学习

    一.Lua语言学习 二.ToLua框架 三.使用ToLua框架对Lua&C#进行交互 四.使用Lua对GameObj进行操作 五.将游戏对象打包&解包 六.将Lua脚本文件进行打包&a ...

  3. 【游戏开发高阶】从零到一教你Unity使用ToLua实现热更新(含Demo工程 | LuaFramework | 增量 | HotUpdate)

    文章目录 零.前言 一.我做的热更新Demo 1.效果演示 2.流程图 3.工程源码 二.为什么要有热更新 三.Unity如何支持热更新 1.热更C#代码 2.热更lua代码与资源 四.Unity中集 ...

  4. 从零搭建React全家桶框架教程

    从零搭建React全家桶框架教程 源码地址:https://github.com/brickspert/react-family 欢迎star 提问反馈:blog 原文地址:https://githu ...

  5. Unity 2D游戏开发教程之摄像头追踪功能

    Unity 2D游戏开发教程之摄像头追踪功能 上一章,我们创建了一个简单的2D游戏.此游戏中的精灵有3个状态:idle.left和right.这看起来确实很酷!但是仅有的3个状态却限制了精灵的能力,以 ...

  6. Asp.net Ajax框架教程

    目录 (一).概述... (二).应用场景代码示例... 1).ScriptManager控件示例...     1. 在异步调用服务端注册客户端脚本新方法...     2. 捕获Ajax异步调用中 ...

  7. Unity插件-NGUI使用教程

    Unity插件-NGUI使用教程 本文提供全流程,中文翻译. Chinar坚持将简单的生活方式,带给世人! (拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) 1 NGUI 一款强大 ...

  8. Unity 2D游戏开发教程之游戏中精灵的跳跃状态

    Unity 2D游戏开发教程之游戏中精灵的跳跃状态 精灵的跳跃状态 为了让游戏中的精灵有更大的活动范围,上一节为游戏场景添加了多个地面,于是精灵可以从高的地面移动到低的地面处,如图2-14所示.但是却 ...

  9. Unity 2D游戏开发教程之为游戏场景添加多个地面

    Unity 2D游戏开发教程之为游戏场景添加多个地面 为游戏场景添加多个地面 显然,只有一个地面的游戏场景太小了,根本不够精灵四处活动的.那么,本节就来介绍一种简单的方法,可以为游戏场景添加多个地面. ...

  10. Unity 2D游戏开发教程之精灵的死亡和重生

    Unity 2D游戏开发教程之精灵的死亡和重生 精灵的死亡和重生 目前为止,游戏项目里的精灵只有Idle和Walking这两种状态.也就是说,无论精灵在游戏里做什么,它都不会进入其它的状态,如死亡.于 ...

最新文章

  1. 编程以外积累: 如何给项目生成类似VS2008的说明文档
  2. html支持1080p,1080p完美支持
  3. 使用Wamp搭建Php本地开发环境,HBuilder调试
  4. python读取txt文件内容-python读取压缩包里面所有*.txt文件的内容
  5. Transformation available that removes all elements from form message type
  6. html复选框值改变后事件,javascript – 从onclick/onchange事件获取HTML值的复选框
  7. 用OC和Swift一起说说二叉树
  8. Django - 中间件
  9. 一个牛逼的项目开发过程是怎样的?
  10. mysql 1067 win7,大师练习win7系统无法启动MySQL服务错误1067的设置教程
  11. 华为广域网帧中继(背靠背)配置
  12. Example-Based Facial Rigging
  13. asp.net删除cookie
  14. 长文解析Resnet50的算法原理
  15. kali自带浏览器上不了网的解决办法
  16. 银行测试(1)-我国的银行类型
  17. php的persion是,php创建Persion类,反射过程,反射后使用流程详解
  18. vba excel 开发游戏_自动化神器—VBA
  19. 准大四生,现在是七月中旬,要为秋招准备什么?
  20. R语言基础入门(全)

热门文章

  1. Installshield 静默安装
  2. Android 3D画廊
  3. 如何进入DOS系统及常用DOS命令总结
  4. VB.NET 强制删除文件
  5. android定义键盘示例(斗地主或跑得快的记牌器)
  6. SLIC超像素算法学习笔记
  7. 视频教程-金蝶K3 WISE 视频教程-ERP
  8. 互联网广告与计算广告学
  9. ITIL、COBIT、CMMi、ISO、17799框架大揭秘
  10. 中兴网信发布“广义智慧城市顶层设计框架”