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

【Lua】ToLua逻辑热更新

[复制链接]
发表于 2023-1-3 09:08 | 显示全部楼层 |阅读模式
1 前言

Lua基础语法 中系统介绍了 Lua 的语法体系,xLua逻辑热更新 中介绍了 xLua 的应用,本文将进一步介绍 Unity3D 中基于 ToLua 实现逻辑热更新。
        逻辑热更新是指:在保持程序正常运行的情况下,在后台修改代码逻辑,修改完成并推送到运行主机上,主机无缝接入更新后的代码逻辑。Unity3D 中,基于 Lua 的逻辑热更新方案主要有 ToLua、xLua、uLua、sLua,本文将介绍 ToLua 逻辑热更新方案。
1)热更新的好处
    不用浪费流量重新下载;不用通过商店审核,版本迭代更加快捷;不用重新安装,用户可以更快体验更新的内容。
2)ToLua 插件下载
    github:https://github.com/topameng/toluagitcode:https://gitcode.net/mirrors/topameng/tolua
3)ToLua 插件导入
        将插件的 Assets 目录下的所有文件拷贝到项目的 Assets 目录下,如下:


4) 生成 Wrap 文件
        导入插件后,菜单栏会多一个 Lua 窗口,点击 Generate All 会生成一些 Wrap 文件,生成路径见【Assets\Source\Generate】,这些 Wrap 文件是 C# 与 Lua 沟通的桥梁。每次生成文件时,建议先点击下 Clear wrap files,再点击 Generate All。


5)配置 wrap 文件
        用户如果想添加新的 wrap 配置,可以在【Assets\Editor\CustomSettings.cs】文件里添加,并且需要在菜单栏里重新点击 Generate All,在【Assets\Source\Generate】中查看是否生成了相应的文件。
6)官方Demo


        本文基于官方 Demo 讲解 ToLua 的使用。
2 ToLua 应用

2.1 C# 中执行 Lua 代码串

HelloWorld.cs
  1. using UnityEngine;
  2. using LuaInterface;
  3. public class HelloWorld : MonoBehaviour {
  4.     void Awake()
  5.     {
  6.         LuaState lua = new LuaState();
  7.         lua.Start();
  8.         string luaStr = @"print('Hello World')";
  9.         lua.DoString(luaStr, "HelloWorld.cs");
  10.         lua.CheckTop();
  11.         lua.Dispose();
  12.         lua = null;
  13.     }
  14. }
复制代码
       运行如下:


2.2 C# 中调用 Lua 文件

ScriptFromFile.cs
  1. using UnityEngine;
  2. using LuaInterface;
  3. public class ScriptFromFile : MonoBehaviour {
  4.     private LuaState lua = null;
  5.     private void Start() {
  6.         lua = new LuaState();
  7.         lua.Start();
  8.         lua.AddSearchPath(Application.dataPath + "\\Scenes\\02");
  9.         lua.DoFile("LuaScript.lua");
  10.         lua.CheckTop();
  11.     }
  12.     private void OnApplicationQuit() {
  13.         lua.Dispose();
  14.         lua = null;
  15.     }
  16. }
复制代码
LuaScript.lua
  1. print("Load lua script")
复制代码
2.3 C# 中调用 Lua 函数

CallLuaFunction.cs
  1. using UnityEngine;
  2. using LuaInterface;
  3. using System;
  4. public class CallLuaFunction : MonoBehaviour {
  5.     private LuaState lua = null;
  6.     private LuaFunction luaFunc = null;
  7.     private void Start() {
  8.         lua = new LuaState();
  9.         lua.Start();
  10.         lua.AddSearchPath(Application.dataPath + "\\Scenes\\03");
  11.         lua.DoFile("LuaScript.lua");
  12.         GetFunc();
  13.         print("Invoke1: " + Invoke1());
  14.         print("Invoke2: " + Invoke2());
  15.         print("PCall: " + PCall());
  16.         print("Delegate: " + Delegate());
  17.         lua.CheckTop();
  18.     }
  19.     private void GetFunc() {
  20.         //luaFunc = lua["luaFunc"] as LuaFunction; // 方式一
  21.         luaFunc = lua.GetFunction("luaFunc"); // 方式二
  22.     }
  23.     private string Invoke1() { // 方法一
  24.         string res = luaFunc.Invoke<int, string>(123456);
  25.         return res;
  26.     }
  27.     private string Invoke2() { // 方法二
  28.         string res = lua.Invoke<int, string>("luaFunc", 123456, true);
  29.         return res;
  30.     }
  31.     private string PCall() { // 方法三
  32.         luaFunc.BeginPCall();
  33.         luaFunc.Push(123456);
  34.         luaFunc.PCall();
  35.         string res = luaFunc.CheckString();
  36.         luaFunc.EndPCall();
  37.         return res;
  38.     }
  39.     // 需要在CustomSettings.cs的customDelegateList里面添加"_DT(typeof(System.Func<int, string>))", 并且重新点击菜单窗口的Lua->Generate All
  40.     private string Delegate() { // 方法四
  41.         DelegateFactory.Init();
  42.         Func<int, string> func = luaFunc.ToDelegate<Func<int, string>>();
  43.         string res = func(123456);
  44.         return res;
  45.     }
  46.     private void Call() { // 方法五(lua中无返回值的函数可以调用此方法)
  47.         luaFunc.Call(123456);
  48.     }
  49.     private void OnApplicationQuit() { // 退出应用回调
  50.         if (luaFunc != null) {
  51.             luaFunc.Dispose();
  52.             luaFunc = null;
  53.         }
  54.         lua.Dispose();
  55.         lua = null;
  56.     }
  57. }
复制代码
LuaScript.lua
  1. function luaFunc(num)                        
  2.         return "num=" .. num
  3. end
复制代码
       打印如下:


2.4 C# 中调用 Lua 变量

AccessLuaVar.cs
  1. using UnityEngine;
  2. using LuaInterface;
  3. public class AccessLuaVar : MonoBehaviour {
  4.     private LuaState lua = null;
  5.     private void Start() {
  6.         lua = new LuaState();
  7.         lua.Start();
  8.         lua.AddSearchPath(Application.dataPath + "\\Scenes\\04");
  9.         InitValue();
  10.         AccessTab();
  11.         lua.CheckTop();
  12.     }
  13.     private void InitValue() { // 在执行lua脚本前, 给lua脚本中变量赋值
  14.         lua["var"] = 5;
  15.         lua.DoFile("LuaScript.lua");
  16.     }
  17.     private void AccessTab() { // 访问tab
  18.         LuaTable tab = lua.GetTable("tab"); // 获取table
  19.         object[] list = tab.ToArray(); // 遍历table元素
  20.         for (int i = 0; i < list.Length; i++)
  21.         {
  22.             print("tab[" + i + "]=" + list[i]);
  23.         }
  24.         print("a=" + tab["a"]);
  25.         tab["a"] = "yyy";
  26.         print("a=" + tab["a"]);
  27.         LuaTable map = (LuaTable) tab["map"];
  28.         print("name=" + map["name"] + ", age=" + map["age"]);
  29.         map.Dispose();
  30.         tab.Dispose();
  31.     }
  32.     private void OnApplicationQuit() { // 退出应用回调
  33.         lua.Dispose();
  34.         lua = null;
  35.     }
  36. }
复制代码
LuaScript.lua
  1. print('var='..var)
  2. tab = {1, 2, 3, a = "xxx"}
  3. tab.map = {name = "zhangsan", age = 23}
复制代码
       打印如下:


2.5 Lua 中使用协程

1)方法一
TestCoroutine.cs
  1. using UnityEngine;
  2. using LuaInterface;
  3. //方法一和方法二展示的两套协同系统勿交叉使用,此为推荐方案
  4. public class TestCoroutine : MonoBehaviour {
  5.     private LuaState lua = null;
  6.     private LuaLooper looper = null;
  7.     private LuaFunction func = null;
  8.         private void Awake() {
  9.         lua  = new LuaState();
  10.         lua.Start();
  11.         lua.AddSearchPath(Application.dataPath + "\\Scenes\\05");
  12.         LuaBinder.Bind(lua);      
  13.         looper = gameObject.AddComponent<LuaLooper>();
  14.         looper.luaState = lua;
  15.         lua.DoFile("LuaScript.lua");
  16.         func = lua.GetFunction("TestCortinue");
  17.         func.Call();
  18.     }
  19.     private void OnApplicationQuit() {
  20.         if (func != null) {
  21.             func.Dispose();
  22.             func = null;
  23.         }
  24.         looper.Destroy();
  25.         lua.Dispose();
  26.         lua = null;
  27.     }
  28. }
复制代码
LuaScript.lua
  1. function CortinueFunc()
  2.         local www = UnityEngine.WWW("http://www.baidu.com")
  3.         coroutine.www(www)
  4.         local str = tolua.tolstring(www.bytes)
  5.         print(str:sub(1, 128))
  6.         print("current frameCount: "..Time.frameCount)
  7.         coroutine.step() --挂起协程, 下一帧继续执行
  8.         print("yield frameCount: "..Time.frameCount)
  9. end
  10. function TestCortinue()       
  11.     coroutine.start(CortinueFunc)
  12. end
复制代码
       打印如下:


2)方法二
TestCoroutine.cs
  1. using UnityEngine;
  2. using LuaInterface;
  3. //方法一和方法二展示的两套协同系统勿交叉使用,类unity原生,大量使用效率低
  4. public class TestCoroutine : LuaClient {
  5.     protected override void OnLoadFinished() {
  6.         base.OnLoadFinished();
  7.         luaState.AddSearchPath(Application.dataPath + "\\Scenes\\05");
  8.         luaState.DoFile("LuaScript.lua");
  9.         LuaFunction func = luaState.GetFunction("TestCortinue");
  10.         func.Call();
  11.         func.Dispose();
  12.     }
  13.     private new void OnApplicationQuit() {
  14.         base.OnApplicationQuit();
  15.     }
  16. }
复制代码
       说明: LuaClient 继承 MonoBehaviour。
LuaScript.lua
  1. function CortinueFunc()
  2.     WaitForSeconds(1)
  3.     print('WaitForSeconds end time: '.. UnityEngine.Time.time)
  4.     WaitForFixedUpdate()
  5.     print('WaitForFixedUpdate end frameCount: '..UnityEngine.Time.frameCount)
  6.     WaitForEndOfFrame()
  7.     print('WaitForEndOfFrame end frameCount: '..UnityEngine.Time.frameCount)
  8.     Yield(null)
  9.     print('yield null end frameCount: '..UnityEngine.Time.frameCount)
  10.     Yield(0)
  11.     print('yield(0) end frameCime: '..UnityEngine.Time.frameCount)
  12.     local www = UnityEngine.WWW('http://www.baidu.com')
  13.     Yield(www)
  14.     print('yield(www) end time: '.. UnityEngine.Time.time)
  15.     local str = tolua.tolstring(www.bytes)
  16.     print(str:sub(1, 128))
  17. end
  18. function TestCortinue()
  19.     StartCoroutine(CortinueFunc)
  20. end
复制代码
       打印如下:


2.6 C# 给 Lua 传递数组

TestArray.cs
  1. using UnityEngine;
  2. using LuaInterface;
  3. public class TestArray : MonoBehaviour {
  4.     private LuaState lua = null;
  5.     private LuaFunction func = null;
  6.         private void Start() {
  7.         lua  = new LuaState();
  8.         lua.Start();
  9.         lua.AddSearchPath(Application.dataPath + "\\Scenes\\06");
  10.         lua.DoFile("LuaScript.lua");
  11.         int[] array = {1, 2, 3};
  12.         func = lua.GetFunction("TestArray");
  13.         Call(array);
  14.         //LazyCall(array);
  15.         lua.CheckTop();
  16.     }
  17.     private void Call(int[] array) { // 方式一
  18.         func.BeginPCall();
  19.         func.Push(array);
  20.         func.PCall();
  21.         double arg1 = func.CheckNumber();
  22.         string arg2 = func.CheckString();
  23.         bool arg3 = func.CheckBoolean();
  24.         func.EndPCall();
  25.         print("arg1: " + arg1 + ", arg2: " + arg2 + ", arg3: " + arg3);
  26.     }
  27.     private void LazyCall(int[] array) { // 方式二
  28.         object[] objs = func.LazyCall((object)array);
  29.         if (objs != null) {
  30.             print("objs[0]: " + objs[0] + ", objs[1]: " + objs[1] + ", objs[2]: " + objs[2]);
  31.         }
  32.     }
  33.     private void OnApplicationQuit() {
  34.         if (func != null) {
  35.             func.Dispose();
  36.             func = null;
  37.         }
  38.         lua.Dispose();
  39.         lua = null;
  40.     }
  41. }
复制代码
LuaScript.lua
  1. function TestArray(array)
  2.     local len = array.Length
  3.     --通过下标遍历数组
  4.     for i = 0, len - 1 do
  5.         print('Array: '..tostring(array[i]))
  6.     end
  7.     --通过迭代器遍历数组
  8.     local iter = array:GetEnumerator()
  9.     while iter:MoveNext() do
  10.         print('iter: '..iter.Current)
  11.     end
  12.     --通过表格遍历数组
  13.     local t = array:ToTable()
  14.     for i = 1, #t do
  15.         print('table: '.. tostring(t[i]))
  16.     end
  17.     --二分查找数组元素, 返回元素下标, 若数组中不存在该元素, 返回负数
  18.     local pos = array:BinarySearch(3)
  19.     print('array BinarySearch, pos='..pos..', value='..array[pos])
  20.     --查找数组元素下标
  21.     pos = array:IndexOf(4)
  22.     print('array indexof, pos='..pos)
  23.     --返回多值
  24.     return 1, '123', true
  25. end
复制代码
2.7 C# 给 Lua 传递字典

TestDictionary.cs
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using LuaInterface;
  4. public sealed class Account {
  5.     public int id;
  6.     public string name;
  7.     public int sex;
  8.     public Account(int id, string name, int sex) {
  9.         this.id = id;
  10.         this.name = name;
  11.         this.sex = sex;
  12.     }
  13. }
  14. public class TestDictionary : MonoBehaviour {
  15.     private LuaState lua = null;
  16.     private LuaFunction func = null;
  17.     private Dictionary<int, Account> map = new Dictionary<int, Account>();
  18.         private void Awake() {
  19.         InitDirec();
  20.         lua = new LuaState();
  21.         lua.Start();
  22.         LuaBinder.Bind(lua);
  23.         lua.AddSearchPath(Application.dataPath + "\\Scenes\\07");
  24.         lua.DoFile("LuaScript.lua");
  25.         func = lua.GetFunction("TestDict");
  26.         Call();
  27.     }
  28.     private void Call() {
  29.         func.BeginPCall();
  30.         func.Push(map);
  31.         func.PCall();
  32.         func.EndPCall();
  33.     }
  34.     private void InitDirec() {
  35.         map.Add(1, new Account(1, "张三", 0));
  36.         map.Add(2, new Account(2, "李四", 1));
  37.         map.Add(3, new Account(3, "王五", 0));
  38.     }
  39.     private void OnApplicationQuit() {
  40.         if (func != null) {
  41.             func.Dispose();
  42.             func = null;
  43.         }
  44.         lua.Dispose();
  45.         lua = null;
  46.     }
  47. }
复制代码
LuaScript.lua
  1. function TestDict(map)
  2.     --遍历map的所有value元素
  3.     local iter = map:GetEnumerator()
  4.     while iter:MoveNext() do
  5.         local v = iter.Current.Value
  6.         print('id: '..v.id ..', name: '..v.name..', sex: '..v.sex)
  7.     end
  8.     --遍历map的key集
  9.     local keys = map.Keys
  10.     iter = keys:GetEnumerator()
  11.     print('------------print dictionary keys---------------')
  12.     while iter:MoveNext() do
  13.         print(iter.Current)
  14.     end
  15.     --遍历map的value集
  16.     local values = map.Values
  17.     iter = values:GetEnumerator()
  18.     print('------------print dictionary values---------------')
  19.     while iter:MoveNext() do
  20.         print(iter.Current.name)
  21.     end
  22.     --根据key查找value元素
  23.     local flag, account = map:TryGetValue(1, nil)
  24.     if flag then
  25.         print('TryGetValue result ok: '..account.name)
  26.     end
  27.     print('kick '..map[2].name)
  28.     --移除元素, 并打印剩余的value
  29.     map:Remove(2)
  30.     iter = map:GetEnumerator()
  31.     while iter:MoveNext() do
  32.         local v = iter.Current.Value
  33.         print('id: '..v.id ..' name: '..v.name..' sex: '..v.sex)
  34.     end
  35. end
复制代码
       补充:CustomSettings.cs 里需要配置 Account 如下:
  1.     //在这里添加你要导出注册到lua的类型列表
  2.     public static BindType[] customTypeList = {               
  3.         //------------------------为例子导出--------------------------------
  4.         _GT(typeof(Account)),
  5.         _GT(typeof(Dictionary<int, Account>)).SetLibName("AccountMap"),
  6.         _GT(typeof(KeyValuePair<int, Account>)),
  7.         _GT(typeof(Dictionary<int, Account>.KeyCollection)),
  8.         _GT(typeof(Dictionary<int, Account>.ValueCollection)),
  9.         //-------------------------------------------------------------------
  10.         ...
  11.     }
复制代码
       配置后,需要点击 Generate All 生成相关 Wrap 文件,如果出现以下报错:
  1. LuaException: LuaScript.lua:12: field or property MoveNext does not exist
复制代码
       修改 System_Collections_Generic_Dictionary_int_Account_KeyCollectionWrap.cs 和 System_Collections_Generic_Dictionary_int_Account_ValueCollectionWrap.cs 文件,将 ToLua.PushValue(L, o) 替换为 ToLua.Push(L, o),如下:
  1. //ToLua.PushValue(L, o);
  2. ToLua.Push(L, o);
复制代码
2.8 C# 给 Lua 传递列表

TestList.cs
  1. using UnityEngine;
  2. using LuaInterface;
  3. using System.Collections.Generic;
  4. public class TestList : MonoBehaviour {
  5.     private LuaState lua = null;
  6.     private void Awake() {
  7.         lua = new LuaState();
  8.         lua.Start();
  9.         LuaBinder.Bind(lua);
  10.         lua.AddSearchPath(Application.dataPath + "\\Scenes\\08");
  11.         lua.DoFile("LuaScript.lua");
  12.         DelegateFactory.Init();
  13.         List<string> list = GetList();
  14.         Call(list);
  15.     }
  16.     private void Call(List<string> list) {
  17.         LuaFunction func = lua.GetFunction("TestList");
  18.         func.BeginPCall();
  19.         func.Push(list);
  20.         func.PCall();
  21.         func.EndPCall();
  22.         func.Dispose();
  23.         func = null;
  24.     }
  25.     private List<string> GetList() {
  26.         List<string> list = new List<string>();
  27.         list.Add("zhang");
  28.         list.Add("li");
  29.         list.Add("wang");
  30.         return list;
  31.     }
  32.     private void OnApplicationQuit() {
  33.         lua.Dispose();
  34.         lua = null;
  35.     }
  36. }
复制代码
        LuaScript.lua
  1. function printList(list)
  2.     str = ""
  3.     for i = 0, list.Count - 1 do
  4.         str = str..(list[i])..", "
  5.     end
  6.     print(str)
  7. end
  8. function TestList(list)
  9.     printList(list) --zhang, li, wang,
  10.     list:Add("chen")
  11.     printList(list) --zhang, li, wang, chen,
  12.     list:Sort()
  13.     printList(list) --chen, li, wang, zhang,
  14.     print("index="..list:IndexOf("wang")) --index=2
  15.     list:Remove("li")
  16.     printList(list) --chen, wang, zhang,
  17. end
复制代码
2.9 Lua 中创建 GameObject 并获取和添加组件

TestGameObject.cs
  1. using UnityEngine;
  2. using LuaInterface;
  3. public class TestGameObject : MonoBehaviour {
  4.     private LuaState lua = null;
  5.         private void Awake() {
  6.         lua = new LuaState();
  7.         lua.Start();
  8.         LuaBinder.Bind(lua);
  9.         lua.AddSearchPath(Application.dataPath + "\\Scenes\\09");
  10.         lua.DoFile("LuaScript.lua");
  11.     }
  12.     private void OnApplicationQuit() {
  13.         lua.Dispose();
  14.         lua = null;
  15.     }
  16. }
复制代码
LuaScript.lua
  1. local GameObject = UnityEngine.GameObject
  2. local PrimitiveType = UnityEngine.PrimitiveType --需要在CustomSettings.cs里添加"_GT(typeof(PrimitiveType))"
  3. local MeshRenderer = UnityEngine.MeshRenderer
  4. local Color = UnityEngine.Color
  5. local Rigidbody = UnityEngine.Rigidbody
  6. go = GameObject.CreatePrimitive(PrimitiveType.Cube)
  7. go:GetComponent("MeshRenderer").sharedMaterial.color = Color.red
  8. rigidbody = go:AddComponent(typeof(Rigidbody))
  9. rigidbody.mass = 1000
复制代码
3 Lua Hook MonoBehaviour 生命周期方法

        MonoBehaviour 生命周期方法见→MonoBehaviour的生命周期。
TestLife.cs
  1. using UnityEngine;
  2. using LuaInterface;
  3. using System.Collections.Generic;
  4. public class TestLife : MonoBehaviour {
  5.     private LuaState lua = null;
  6.     private Dictionary<string, LuaFunction> func;
  7.     private void Awake() {
  8.         lua = new LuaState();
  9.         lua.Start();
  10.         LuaBinder.Bind(lua);
  11.         lua.AddSearchPath(Application.dataPath + "\\Scenes\\10");
  12.         lua.DoFile("LuaScript.lua");
  13.         GetFunc();
  14.         CallFunc("awake");
  15.     }
  16.     private void OnEnable() {
  17.         CallFunc("onEnable");
  18.     }
  19.     private void Start() {
  20.         CallFunc("start");
  21.     }
  22.     private void Update() {
  23.         CallFunc("update");
  24.     }
  25.     private void OnDisable() {
  26.         CallFunc("onDisable");
  27.     }
  28.     private void OnDestroy() {
  29.         CallFunc("onDestroy");
  30.     }
  31.     private void GetFunc() {
  32.         func = new Dictionary<string, LuaFunction>();
  33.         AddFunc("awake");
  34.         AddFunc("onEnable");
  35.         AddFunc("start");
  36.         AddFunc("update");
  37.         AddFunc("onDisable");
  38.         AddFunc("onDestroy");
  39.     }
  40.     private void AddFunc(string funcName) {
  41.         LuaFunction fun = lua.GetFunction(funcName);
  42.         if (fun != null) {
  43.             func.Add(funcName, fun);
  44.         }
  45.     }
  46.     private void CallFunc(string funcName) {
  47.         if (func.ContainsKey(funcName)) {
  48.             LuaFunction fun = func[funcName];
  49.             fun.Call();
  50.         }
  51.     }
  52.     private void OnApplicationQuit() {
  53.         foreach(var fun in func.Values)
  54.         {
  55.             fun.Dispose();
  56.         }
  57.         func.Clear();
  58.         func = null;
  59.         lua.Dispose();
  60.         lua = null;
  61.     }
  62. }
复制代码
LuaScript.lua
  1. function awake()
  2.     print("awake")
  3. end
  4. function onEnable()
  5.     print("onEnable")
  6. end
  7. function start()
  8.     print("start")
  9. end
  10. function update()
  11.     print("update")
  12. end
  13. function onDisable()
  14.     print("onDisable")
  15. end
  16. function onDestroy()
  17.     print("onDestroy")
  18. end
复制代码

本帖子中包含更多资源

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

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

本版积分规则

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

GMT+8, 2024-11-24 13:04 , Processed in 0.090933 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

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