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 即可

  • 編輯器擴展提供一個按鈕


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章