小言_互联网的博客

【Lua】ToLua逻辑热更新

375人阅读  评论(0)

1 前言

        Lua基础语法 中系统介绍了 Lua 的语法体系,本文将进一步介绍 Unity3D 中基于 ToLua 实现逻辑热更新。

        逻辑热更新是指:在保持程序正常运行的情况下,在后台修改代码逻辑,修改完成并推送到运行主机上,主机无缝接入更新后的代码逻辑。

        Unity3D 中,基于 Lua 的逻辑热更新方案主要有 ToLua、XLua、uLua、sLua,本文将介绍 ToLua 逻辑热更新方案。

        1)ToLua 插件下载

        2)ToLua 插件导入

        将插件的 Assets 目录下的所有文件拷贝到项目的 Assets 目录下,如下:

        3) 生成 Wrap 文件

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

        4)配置 wrap 文件

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

        5)官方Demo

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

2 ToLua 应用

2.1 Unity3D 中执行 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 Unity3D 中调用 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

print("Load lua script")

2.3 Unity3D 中调用 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 Unity3D 中调用 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 Unity3D 给 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 Unity3D 给 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 文件,如果出现以下报错:

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 Unity3D 给 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 Unity3D 生命周期方法

        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

转载:https://blog.csdn.net/m0_37602827/article/details/128429135
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场