title: unity-lua运行时重载重启
categories: Unity3d
tags: [unity, lua, runtime, reload, restart]
date: 2020-06-30 11:20:08
comments: false
mathjax: true
toc: true
开发环境中, 在不停止运行游戏的情况下, 刷新修改后的脚本逻辑, 提升开发效率
lua 虚拟机重启
就是销毁掉 旧的 luastate, 实例化新的 luastate
另一个使用场景是在热更完之后, 需要重启 lua 虚拟机, 使最新的 lua 脚本生效
-
csharp 重启逻辑
// 重启 lua 虚拟机 public void RestartLua() { StartCoroutine(RestartLuaEngine()); } IEnumerator RestartLuaEngine() { yield return 1; if (_luaMgr != null) { GameObject.Destroy(_luaMgr); yield return 1; _luaMgr = gameObject.AddComponent<LuaMgr>(); yield return StartLuaMain(); } } IEnumerator StartLuaMain() { yield return 1; // 跳一帧执行lua LuaMgr.GetMainState().DoFile("main.lua"); _luaMgr.OnStart(); CallLuaFunction("Main"); }
-
编辑器扩展提供一个按钮
lua 重载
重载只是刷新脚本的方法, 使修改后的方法逻辑生效
原理:
- 将 require 方法保存起来, 然后重写 require 逻辑
- 在 require 逻辑中捕获需要重载的 lua 文件
- 重新加载 这些 lua 文件.
例如:
-
某个 class
CUIPnlSettingLogic = CUIPnlSettingLogic or class() -- 这里使用全局变量 CUIPnlSettingLogic.__name = "CUIPnlSettingLogic" -- 类名标记 function CUIPnlSettingLogic.Init(self, id, obj) print("--- CUIPnlSettingLogic.Init 111") end return CUIPnlSettingLogic
-
重写 require
local ipairs = ipairs local pairs = pairs local string = string local print = print local orgRequire = require local error = error local checkTbl = { "logic.", "module.", "main", "tolua_patch", } local ignoreTbl = { "debug_unit_test", } local notUsTbl = {} -- 不是我们的 lua 文件 local usTbl1 = {} -- 有 xxx.__name local usTbl2 = {} -- 无 xxx.__name local function checkSameName(name, path) for k,v in pairs(usTbl1) do if name == v then error(string.format("--- same name: %s\npath1: %s\npath2: %s", name, k, path)) end end end local function isCheck(path) for k,v in ipairs(checkTbl) do local i, _ = string.find(path, v) if i ~= nil then return true end end return false end local function isIgnore(path) for k,v in ipairs(ignoreTbl) do local i, _ = string.find(path, v) if i ~= nil then return true end end end require = function(path) local retTbl = orgRequire(path) if isIgnore(path) or not isCheck(path) then notUsTbl[path] = true return retTbl end if usTbl1[path] or usTbl2[path] then return retTbl end if type(retTbl) == "table" and retTbl.__name then checkSameName(retTbl.__name, path) usTbl1[path] = retTbl.__name else usTbl2[path] = true end return retTbl end gDumpReqTbl = function() dump(usTbl1, "--- usTbl1, has xxx.__name") dump(usTbl2, "--- usTbl2, no xxx.__name") dump(notUsTbl, "--- notUsTbl, has xxx.__name") end gRefreshReqTbl = function() local usTmpTbl1 = usTbl1 usTbl1 = {} for k,_ in pairs(usTmpTbl1) do package.loaded[k] = nil end local usTmpTbl2 = usTbl2 usTbl2 = {} for k,_ in pairs(usTmpTbl2) do package.loaded[k] = nil end for k,_ in pairs(usTmpTbl1) do require(k) end for k,_ in pairs(usTmpTbl2) do require(k) end gLog("<color=#00ff00ff>--- 重载 lua ok</color>") end
需要刷的时候调用一下
gRefreshReqTbl
即可 -
编辑器扩展提供一个按钮