Unity XLua熱更新

JK Chen發表於2021-01-02

持續更新

相關連結

基礎案例

0基礎的先看 xlua遷入專案

/* 
 *  Author : Jk_Chen
 */

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
/// <summary>
/// 熱更新類,進行程式碼的修復
/// </summary>
public class XLuaFix : MonoBehaviour
{
    static LuaEnv lua;
    static int tmp = 2;
    void Awake()
    {
        lua = new LuaEnv();
        // 簡單執行Lua語句
        lua.DoString("print 'lua print'");
        // 呼叫C#類的公有靜態方法
        lua.DoString("CS.UnityEngine.Debug.Log('cs debug')");
        lua.DoString("CS.XLuaFix.Print()");
        // 先讓某個類的私有成員可訪問,再呼叫私有靜態方法
        lua.DoString("xlua.private_accessible(CS.XLuaFix)");
        lua.DoString("CS.XLuaFix.PrivatePrint()");
        // 修改靜態變數
        lua.DoString("CS.XLuaFix.tmp=3");
        lua.DoString("print('修改後的靜態變數為:' .. CS.XLuaFix.tmp)");
    }

    public static void Print()
    {
        Debug.Log("public static");
    }

    private static void PrivatePrint()
    {
        Debug.Log("private static");
    }

    private void OnDestroy()
    {
        lua.Dispose();
    }
}

在這裡插入圖片描述

關於XLua的生成程式碼和程式碼注入

程式碼需要進行熱更新的時候,需要進行生成程式碼和程式碼注入。

請在每次修改程式碼後(執行前)都執行一遍該操作。

程式碼注入後Debug會比較麻煩,因為Log內尋找不到是哪行程式碼的執行結果。

看得到,但是不能通過點選直接找到哪行
在這裡插入圖片描述
原來是這樣的
在這裡插入圖片描述

如果不需要熱更新可以把生成的程式碼Clear掉

簡單熱更新

必要步驟

  • 需要熱更新的類前加[Hotfix]
  • 執行前生成程式碼和程式碼注入
/* 
 *  Author : Jk_Chen
 */

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
/// <summary>
/// 熱更新類,進行程式碼的修復
/// </summary>
[Hotfix]
public class XLuaFix : MonoBehaviour
{
    static LuaEnv lua;
    public static int tmp = 2;

    void Awake()
    {
        lua = new LuaEnv();
        // 呼叫帶參函式
        lua.DoString("CS.XLuaFix.Print(CS.XLuaFix.tmp)");
        // 修改函式
        lua.DoString(
            @"xlua.hotfix(CS.XLuaFix,'Print',function(value)
                CS.UnityEngine.Debug.Log('After ' .. value*value)
            end)"
        );
        // 呼叫修改後的函式
        lua.DoString("CS.XLuaFix.Print(CS.XLuaFix.tmp)");
    }

    public static void Print(int value)
    {
        Debug.Log("Defore " + value);
    }

    private void OnDestroy()
    {
        lua.Dispose();
    }
}


在這裡插入圖片描述
程式碼修復成功,但是報錯了。這個錯誤官方有回答:

這是由於C#還存在指向lua虛擬機器裡頭某個函式的delegate,為了防止業務在虛擬機器釋放後呼叫這些無效(因為其引用的lua函式所在虛擬機器都釋放了)delegate導致的異常甚至崩潰,做了這個檢查。
怎麼解決?釋放這些delegate即可,所謂釋放,在C#中,就是沒有引用:
你是在C#通過LuaTable.Get獲取並儲存到物件成員,賦值該成員為null;
你是在lua那把lua函式註冊到一些事件事件回撥,反註冊這些回撥;
如果你是通過xlua.hotfix(class, method, func)注入到C#,則通過xlua.hotfix(class, method, nil)刪除;
要注意以上操作在Dispose之前完成。

所以我們在釋放虛擬機器之前釋放這些委託即可:

    private void OnDestroy()
    {
        // 釋放委託
        lua.DoString(
            @"xlua.hotfix(CS.XLuaFix,'Print',nil)"
        );
        lua.Dispose();
    }

使用Lua指令碼檔案

之前使用到的都是在C#程式碼內使用DoString來呼叫Lua,這樣有一個很麻煩的地方就是,由於每次都會修改程式碼,所以都需要生成和注入。而且和我們的熱更新也不搭嘎。

所以我們現在使用指令碼檔案的方式。require函式可以載入一個lua指令碼,但如果lua指令碼不在Resources路徑下,或者不在預設的目錄下,此時是載入不到對應的lua指令碼的。

我們需要自己寫一個loader來適配lua指令碼的位置:

c#程式碼:

/* 
 *  Author : Jk_Chen
 */

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
/// <summary>
/// 熱更新類,進行程式碼的修復
/// </summary>
[Hotfix]
public class XLuaFix : MonoBehaviour
{
    static LuaEnv lua;
    public static int tmp = 2;

    void Awake()
    {
        lua = new LuaEnv();
        // 定義loader,從Assets/Lua目錄下讀取,已自帶.lua.txt
        // 例如讀取Assets/Lua/xx.lua.txt只需要 lua.DoString("require('xx')");
        lua.AddLoader((ref string file) => {
            string path = System.IO.Path.Combine("Assets/Lua", file + ".lua.txt");
            if (System.IO.File.Exists(path))
            {
                return System.IO.File.ReadAllBytes(path);
            }
            else
            {
                return null;
            }
        });
        lua.DoString("require('XLuaFix')");
    }

    public static void Print(int value)
    {
        Debug.Log("Defore " + value);
    }

    private void OnDestroy()
    {
        // 釋放委託
        lua.DoString("require('XLuaFixDispose')");
        // 釋放虛擬機器
        lua.Dispose();
    }
}


lua指令碼: XLuaFix.lua.txt

-- 呼叫帶參函式
CS.XLuaFix.Print(CS.XLuaFix.tmp)
-- 修改函式
xlua.hotfix(CS.XLuaFix,'Print',function(value)
	CS.UnityEngine.Debug.Log('After ' .. value*value)
end)
-- 呼叫修改後的函式
CS.XLuaFix.Print(CS.XLuaFix.tmp)

lua指令碼: XLuaFixDispose.lua.txt

-- 釋放委託
xlua.hotfix(CS.XLuaFix,'Print',nil)

本人是用notepad寫lua指令碼的在這裡插入圖片描述
notepad配置部落格

相關文章