XLua基礎


XLua簡介

Xlua是騰訊研發的一款Lua開源插件,為Unity、 .Net、 Mono等C#環境增加Lua腳本編程的能力,借助xLua,這些Lua代碼可以方便的和C#相互調用,在游戲中,該技術多用於熱更新。可以在GitHub上搜索XLua進行下載,如果網速太慢,也可以在gitee上下載

 

C#執行Lua腳本

我們在學習每一個課程的時候,最先接觸的都是“Hello world”,在XLua中,如何執行該語句呢?將下載的Xlua項目打開,並且創建一個新的腳本,引入XLua命名空間,並掛載到游戲物體。

 //聲明一個Lua虛擬機
   public static LuaEnv lua;

   private void Start()
  {
       //對虛擬機進行實例化
       lua = new LuaEnv();
       //用C#的UnityEngine.Debug.Log打印日志
       lua.DoString("CS.UnityEngine.Debug.Log('Hello world')");
       lua.Dispose();
  }

此時,運行項目,可以看到在控制台中打印出了 Hello world

Tips💁‍♂:一個LuaEnv實例對應Lua虛擬機,出於開銷的考慮,建議全局唯一。

通過Lua.DoString函數,我們可以在Lua語言中快速執行C#代碼,但是當Lua腳本內容較多時,將不是很方便執行,這時可以將腳本寫在Lua文件中,並C#中執行.

首先在Unity中創建Resources文件夾,並在里面創建一個txt文本文件,命名為"Demo1.lua.txt"(其要求的是一個文本文檔,文件名中的.lua並沒有其他含義,僅僅是起標識作用),在文本中加入CS.UnityEngine.Debug.Log("Hello world,load file"),接下來加載該文件

private void Start()
{
   //對虛擬機進行實例化
   lua = new LuaEnv();

   //方式一:通過TextAsset加載
   TextAsset asset = Resources.Load<TextAsset>("LuaDemo1.lua");
   lua.DoString(asset.text);
   
   //方式二:通過Require加載(常用),只能使用Resources與內置的路徑的lua文件,不能自定義路徑
   lua.DoString("require 'LuaDemo1'");
   
   //方式三:自定義Loader加載,使用Require加載有不能自定義路徑等缺點,通過自定義Loader則可以解決上述問題
   lua.AddLoader(DemoLoader);
   lua.DoString("require 'LuaDemo1'");
}

//自定義Loader函數,執行DoString時會自動調用該函數,里面可以實現自己的邏輯
private byte[] DemoLoader(ref string filepath)
{
   string path = filepath + ".lua";
   TextAsset asset = Resources.Load<TextAsset>(path);

   return asset.bytes;
}

 

C#獲取與修改Lua中的值

獲取Lua中的變量

首先我們修改lua文本文檔LuaDemo1中的內容,定義下面兩個變量

    x = 123;
   y = "456"

接下來在C#中獲取x和y的值,Global是Lua中的一個全局表,里面包含了所有的全局變量

    lua.DoString("require 'LuaDemo1'");

//獲取Lua中的值
   int x = lua.Global.Get<int>("x");
   string y = lua.Global.Get<string>("y");

   Debug.Log(x + " , " + y); //打印 123 , 456

//修改Lua中的值
lua.Global.Set("x", 321);
Debug.Log(lua.Global.Get<int>("x"));   //打印 321

 

獲取Lua中的Table

如果Lua腳本中包含Table,又該怎么調用呢?我們在Lua文本中添加Table,如下所示

    x = 123;
   y = "456"
   
--定義person表
   person =
  {
       name = "slayer",
       age = 8,
       "only string",
       999,
       speak = function()
           print("this is a function in Person")
       end
  }

--通過.的方式為person表添加函數
   function person.sayName(self)
       print(self.name)
   end

--通過:的方式為person表添加函數 等價於 person.getAge(self)
   function person:getAge()
       print(self.age)
   end

C#若要獲取Lua中Table值,可以通過類或者接口建立映射,然后獲取其值,下面展示如何使用接口獲取

    private void Start()
  {
       lua.DoString("require 'LuaDemo1'");

       //上面person表中,對於"only string",999這樣單獨的值,可以通過List建立映射
       List<object> list = new List<object>();
       list = lua.Global.Get<List<object>>("person");
       foreach (var val in list)
      {
           Debug.Log(val); //分別打印"only string",999
      }

       IPersonal person = lua.Global.Get<IPersonal>("person");
       Debug.Log(person.name); //輸出 "slayer"
       Debug.Log("Sum:" + person.getSum(12, 34)); //輸出 46
  }    

//定義IPersonal接口,注意這里必須使用 CSharpCallLua 標簽
//並且里面的屬性和方法要和Lua腳本中的person表對應
  [CSharpCallLua]
   public interface IPersonal
  {
       string name { get; set; }
       string age { get; set; }

       void sayName();

       int getSum(int num1, int num2);
  }

 

獲取Lua中的函數

我們先在Lua腳本中定義兩個函數,如下所示:

    function m_Print(msg)
       print(msg)
   end

   function Add(a,b)
       return a + b
   end

要在C#中調用這兩個函數,需要使用委托,對於沒有返回值的,可以使用C#自帶的Action,有返回值則使用Func,當然也可以自定義委托

    lua.DoString("require 'LuaDemo1'");

//通過Action獲取Lua中的映射,並執行該函數
   Action<string> mPrint = lua.Global.Get<Action<string>>("m_Print");
   mPrint("Hello world");

   Func<int, int, int> add = lua.Global.Get<Func<int, int, int>>("Add");
   int ans = add(1, 2);
   Debug.Log("Add ans:" + ans); //輸出 3

 

錯誤信息:InvalidCastException: This type must add to CSharpCallLua:XXXX

執行上述代碼時,可能會出現上述錯誤,該錯誤表面當前使用的類型沒有加上CSharpCallLua標簽,此時我們需要將該標簽添加到對應的地方,正常情況下錯誤將會消失.但有時候,明明添加了該標簽,還是提示這個錯誤,這時通過下面三個步驟一般都可以解決.

  • 方式一:在Unity的Player Setting中將API Compatibility Level設置為 .NET 4.X

     

  • 方式二:點擊XLua -> Clear Generated Code -> Generated Code

     

  • 方式三:在Assets文件夾中,找到XLua -> Examples,找到ExampleGenConfig.cs文件,在CSharpCallLua中按照格式添加你要調用的方法

     

    再不行就只能換一個版本重試了.

 

XLua調用C#靜態方法

  在上一節中我們學習了怎么通過C#調用XLua打印“Hello world”,那么XLua如何調用C#呢?一行代碼輕松搞定

首先創建一個txt文件保存XLua腳本內容,我們命名為“LuaDemo2.lua.txt”,里面的內容如下

--調用C#中Debug.Log函數
--Lua通過CS來訪問C#的代碼,后面的UnityEngine為命名空間
--CS.UnityEngine.Debug.Log("Hello world")

--在寫代碼時經常需要調用CS.UnityEngine里面的函數,為了方便我們可以定義一個變量將其保存起來
--使用local關鍵字定義一個局部變量 下面代碼實現的效果與上面相同
local cu = CS.UnityEngine
cs.Debug.Log("Hello world")

--調用其他方法、字段也與之類似,例如
--print(cu.Time.deltaTime)

要執行這行代碼,我們創建一個C#腳本,並執行Lua文件

lua.DoString("require 'LuaDemo2'");  //打印Hello world

 

XLua訪問自定義的C#類

在C#中定義一個名為People的類,內容如下:

public class People
{
   public string name;
   public int age;

   public void Show()
  {
       Debug.Log(name + ":" + age);
  }
}

接下來在XLua中對其進行訪問

--找到People類並對其進行實例化,等價於:
--local people = CS.People  
--people = people()
local people = CS.People()

people.name = "taylor"
people.age = 23
people:Show()  --打印 taylor:23

 

XLua查找游戲對象

在unity場景中創建一個Cube,怎么在Lua中修改它的名字呢?

local cug = CS.UnityEngine.GameObject
local cube = cug.Find("Cube")
cube.name = "Change"

調用腳本后可以看到,場景中的Cube成功改名為Change

 

XLua實現Unity內置函數,如Awake,Update...

先在Lua腳本中定義一個start函數,函數名稱隨意

function luaStart()
print("this is lua start")
end

在C#中調用它,對於lua元表,簡單說明一下,點擊查看具體定義

Lua 查找一個表元素時的規則,其實就是如下 3 個步驟:

  • 1.在表中查找,如果找到,返回該元素,找不到則繼續

  • 2.判斷該表是否有元表,如果沒有元表,返回 nil,有元表則繼續。

  • 3.判斷元表有沒有 index 方法,如果 index 方法為 nil,則返回 nil;如果 index 方法是一個表,則重復 1、2、3;如果 __index 方法是一個函數,則返回該函數的返回值。

private void Start()
{
   //加載上面的lua腳本
   asset = Resources.Load<TextAsset>("LuaDemo2.lua");

   //定義一個table
   LuaTable table = lua.NewTable();

   //為table設置元表
   LuaTable metaTable = lua.NewTable();
   metaTable.Set("__index", lua.Global);
   table.SetMetaTable(metaTable);
   metaTable.Dispose();

   table.Set("self", this);

   lua.DoString(asset.text, "LuaDemo2.lua", table);

   //通過Action獲取lua中的函數
   Action _luaStart = table.Get<Action>("luaStart");

   //執行,Awake和Update類似,只是在不同的地方調用,例如如果實現了_luaUpdate則在Update函數中調用
   _luaStart();
}

 

XLua控制UI事件

修改前面的luaStart函數為下所示,(注意,C#調用的腳本需要掛在Button按鈕上,因為文中使用了self:GetComponent)

--通過lua為unity中的ui添加事件
function luaStart()
print("This is lua Start")

input = CUG.Find("InputField")

   --查找到按鈕並且為按鈕添加事件
self:GetComponent("Button").onClick:AddListener(function()
val = input:GetComponent("InputField").text

if(val == "")
then
--print("Can't input nil")
input:GetComponent("InputField").text = "Can't input nil"
else
--print("clicked, you input is '" ..val .."'")
input:GetComponent("InputField").text = "clicked, you input is '" ..val .."'"
end
end)
end

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM