unity-lua运行时重载重启


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 重载

重载只是刷新脚本的方法, 使修改后的方法逻辑生效

原理:

  1. 将 require 方法保存起来, 然后重写 require 逻辑
  2. 在 require 逻辑中捕获需要重载的 lua 文件
  3. 重新加载 这些 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 即可

  • 编辑器扩展提供一个按钮


猜你喜欢

转载自blog.csdn.net/yangxuan0261/article/details/107008623