xLua熱更新外掛

movin2333發表於2021-02-03

一.xLua外掛下載安裝

1.從GitHub上搜尋並下載外掛

 

 2.將檔案複製到unity中

 

 

 

 3.檢查是否有錯誤

二.在unity中呼叫lua

1.簡單呼叫

在c#指令碼中使用LuaEnv類可以執行lua,建議LuaEnv例項全域性唯一。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//引入名稱空間
using XLua;

public class HelloLua01 : MonoBehaviour
{
    //宣告luaenv變數
    private LuaEnv luaenv;

    void Start()
    {
        //例項化luaenv變數
        luaenv = new LuaEnv();
        //呼叫DoString方法執行lua程式碼
        luaenv.DoString("print('hello world!')");
    }

    private void OnDestroy()
    {
        //釋放luaenv
        luaenv.Dispose();
    }

}

 

 

 2.lua檔案的讀取執行

using UnityEngine;
//引入名稱空間
using XLua;

public class HelloLua : MonoBehaviour
{
    private LuaEnv luaenv;

    void Start()
    {
        luaenv = new LuaEnv();
        //將lua程式碼檔案放在Resources資料夾下,並新增字尾.txt
        //通過Resources.Load方法讀取這個檔案,宣告型別為TextAsset,這樣讀取的過程中就會自動新增字尾.txt
        //讀取到檔案的內容後轉化為byte陣列(使用tostring方法或者text方法轉化為字串或文字也都可以),並交給DoString方法執行這段程式碼
        luaenv.DoString(Resources.Load<TextAsset>("helloLua.lua").bytes);
    }

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

}

using UnityEngine;
//引入名稱空間
using XLua;

public class HelloLua : MonoBehaviour
{
    private LuaEnv luaenv;

    void Start()
    {
        luaenv = new LuaEnv();
        //使用系統內建的方法載入檔案並執行程式碼,檔名是helloLua.lua.txt
        luaenv.DoString("require 'helloLua'");
    }

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

}

 

 

 3.自定義Loader:針對檔案字尾不是.lua.txt的型別

public class HelloLua : MonoBehaviour
{
    private LuaEnv luaenv;

    void Start()
    {
        luaenv = new LuaEnv();

        //通過AddLoader方法自定義Loader程式,實際上是將自定義的Loader方法新增到一個委託中
        luaenv.AddLoader(MyLoader);
        //執行DoString方法,會首先呼叫自定義的Loader方法,如果返回值為空,再呼叫系統定義的Loader方法
        luaenv.DoString("require 'helloLua'");
    }

    //自定義的Loader方法,這個方法返回值必須是byte陣列,引數是ref的string型別,傳入檔案地址
    private byte[] MyLoader(ref string filePath)
    {
        print(filePath);
        return null;
    }

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

}

public class HelloLua : MonoBehaviour
{
    private LuaEnv luaenv;

    void Start()
    {
        luaenv = new LuaEnv();

        luaenv.AddLoader(MyLoader);
        luaenv.DoString("require 'helloLua'");
    }

    private byte[] MyLoader(ref string filePath)
    {
        //列印文字說明這個loader函式被呼叫
        print("呼叫了自定義的loader");
        //將lua檔案放入streaMingAssetsPath資料夾內,拼接好檔案的完整絕對路徑
        string path = Application.streamingAssetsPath + "/" + filePath + ".lua.txt";
        //讀取檔案,獲得byte陣列並返回
        return System.Text.Encoding.UTF8.GetBytes(File.ReadAllText(path));
    }

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

 

 

 4.訪問lua中的變數

1)訪問值型別變數

public class HelloLua : MonoBehaviour
{
    private LuaEnv luaenv;

    void Start()
    {
        luaenv = new LuaEnv();
        luaenv.DoString("require 'helloLua'");

        //訪問檔案中的全域性int型別變數a
        print(luaenv.Global.Get<int>("a"));
    }

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

2)訪問table形式的變數,可以定義一個對應table的類進行,Get方法會自動new出這個類,並將變數值拷貝進去。

public class HelloLua : MonoBehaviour
{
    private LuaEnv luaenv;

    void Start()
    {
        luaenv = new LuaEnv();
        luaenv.DoString("require 'helloLua'");

        //獲取表person的內容,儲存為Person類
        Person p = luaenv.Global.Get<Person>("person");
        print(p.name + p.age);
    }

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

    //定義一個和表對應好屬性和方法的類
    class Person
    {
        public string name;
        public int age;
    }
}
person = {
 name = 'movin',age = 18
}

 3)也可以定義一個對應table屬性和方法的介面,但是介面需要加上[CSharpCallLua]特性,同時lua中定義函式時第一個引數必須是self或者使用冒號定義

public class HelloLua : MonoBehaviour
{
    private LuaEnv luaenv;

    void Start()
    {
        luaenv = new LuaEnv();
        luaenv.DoString("require 'helloLua'");
        
        IPerson p = luaenv.Global.Get<IPerson>("person");
        print(p.name + p.age);
    }

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

    //如果使用介面定義接收的table,必須加上[CSharpCallLua]特性自動生成程式碼
    [CSharpCallLua]
    interface IPerson
    {
        string name { get; set; }
        int age { get; set; }
    }
}

這裡注意:目前xlua沒有支援unity2019版本,這裡程式碼沒有問題,但是執行始終會報錯。

 

 4)使用字典或者連結串列對應table

public class HelloLua : MonoBehaviour
{
    private LuaEnv luaenv;

    void Start()
    {
        luaenv = new LuaEnv();
        luaenv.DoString("require 'helloLua'");

        //將table轉化為字典
        Dictionary<string,object> dict = luaenv.Global.Get<Dictionary<string, object>>("person");
        //遍歷輸出
        foreach(KeyValuePair<string,object> pair in dict)
        {
            print(pair.Key + pair.Value);
        }
    }

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

 

 5)使用列表對應table,只能儲存table中沒有鍵的value值

public class HelloLua : MonoBehaviour
{
    private LuaEnv luaenv;

    void Start()
    {
        luaenv = new LuaEnv();
        luaenv.DoString("require 'helloLua'");

        //將table轉化為list,list只能儲存沒有鍵的table值
        List<object> list = luaenv.Global.Get<List<object>>("person");
        //遍歷輸出
        foreach(Object o in list)
        {
            print(o);
        }
    }

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

6)使用LuaTable類對應table

public class HelloLua : MonoBehaviour
{
    private LuaEnv luaenv;

    void Start()
    {
        luaenv = new LuaEnv();
        luaenv.DoString("require 'helloLua'");

        //將table儲存為luatable類的物件
        LuaTable tab = luaenv.Global.Get<LuaTable>("person");
        //取得其中的鍵對應的值,需要宣告值型別
        print(tab.Get<string>("name"));
    }

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

 

 7)使用委託對應lua中的函式

public class HelloLua : MonoBehaviour
{
    private LuaEnv luaenv;

    void Start()
    {
        luaenv = new LuaEnv();
        luaenv.DoString("require 'helloLua'");

        //使用委託儲存lua中的函式,函式名稱為add
        Add add = luaenv.Global.Get<Add>("add");
        add(1,3);
    }

    //自定義一個委託對應lua中相應的函式,一定要加上[CSharpCallLua]特性
    [CSharpCallLua]
    delegate void Add(int a, int b);
    private void OnDestroy()
    {
        luaenv.Dispose();
    }
}

 

 2019版本的unity和xlua不相容,使用更低版本解決問題。

如果lua有不止一個返回值,可以使用out引數或ref引數接收其他的返回值。

8)使用LuaFunction對應lua中的函式

public class HelloLua : MonoBehaviour
{
    private LuaEnv luaenv;

    void Start()
    {
        luaenv = new LuaEnv();
        luaenv.DoString("require 'helloLua'");

        //xlua提供了LuaFunction對應lua中的函式,使用Call方法呼叫函式,返回值為一個objec型別的陣列
        LuaFunction func = luaenv.Global.Get<LuaFunction>("add");
        object[] objs = func.Call(1, 3);
        foreach(object o in objs)
        {
            print(o.ToString());
        }
    }

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

 

 三.在lua中呼叫C#

1.簡單實現呼叫

1)首先在空物體上掛載指令碼,指令碼呼叫lua程式碼

public class LuaCallCSharp : MonoBehaviour
{
    private LuaEnv luaEnv;

    void Start()
    {
        luaEnv = new LuaEnv();

        luaEnv.DoString("require 'LuaCallCSharp'");
    }

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

2)然後在lua程式碼中例項化一個遊戲物體

--建立遊戲物體,對應C#中的new UnityEngine.GameObject()程式碼
CS.UnityEngine.GameObject()

3)最後看執行結果

 

 4)注意事項:lua中沒有new關鍵字,所有C#相關的內容都放在了CS下,包括函式、屬性和方法等。對於經常訪問的類,可以先使用區域性變數儲存起來,然後再進行呼叫,減少程式碼量也提高效能。

2.訪問靜態屬性和方法

--對應的C#程式碼:print(UnityEngine.Time.deltaTime);
print(CS.UnityEngine.Time.deltaTime)

--對應的C#程式碼:UnityEngine.GameObject.Find("Main Camera").name = "fixed by lua"
CS.UnityEngine.GameObject.Find("Main Camera").name = "fixed by lua"

 

 3.訪問成員屬性和方法,使用冒號語法糖

local gameObject = CS.UnityEngine.GameObject
local camera = gameObject.Find("Main Camera")

--呼叫成員方法使用冒號語法糖(推薦),或者將自身作為第一個引數傳遞(不推薦)
local cameraComponent = camera:GetComponent("Camera")
gameObject.Destroy(cameraComponent)

4.其他

1)C#中的out引數對映到lua中時不算引數,C#中的out和ref返回值對映到lua中對應多返回值。

2)xlua支援方法的過載

3)支援可變引數

4)lua不支援泛型,但是可以通過Extension methods功能進行封裝後使用

5)C#的委託的呼叫和普通方法相同,註冊或者移除委託中的方法時需要將“+”或者“-”號作為第一個引數傳遞

相關文章