天天看點

xLua學習案例xLua使用案例

xLua使用案例

需引用命名空間XLua

  • 簡單案例1 在c#中直接調用lua代碼
LuaEnv luaEnv = null;
    private void Start()
    {   
        //建立lua的運作環境 為防止記憶體大量消耗 建議全局裡隻存在一個執行個體
        luaEnv = new LuaEnv();
        //需要傳遞一個字元串 内容為lua程式
        luaEnv.DoString("print('helloxLua')");//通過c#調用lua
        luaEnv.DoString(" CS.UnityEngine.Debug.Log('helloxLua') ");//通過lua調用C#
        //luaEnv.Dispose();
    }
    private void OnDestroy()
    {
        //在代碼執行完後釋放luaEnv
        luaEnv.Dispose();
    }
           
  • **簡單案例2 ** 通過加載運作lua源檔案
  1. 通過Resources加載

    注:為能使Resources方法能夠讀取到lua檔案 lua檔案字尾需為.lua.txt

private LuaEnv luaenv;
    private void Start()
    {
        luaenv = new LuaEnv();
        TextAsset ta = Resources.Load<TextAsset>("helloWorld.lua");
        luaenv.DoString(ta.text);
        luaenv.Dispose();
    }
           
  1. 通過loader加載(lua檔案需放在Resources檔案夾 字尾為.lua.txt)

    注(引用自xLua作者原話):

    require實際上是調一個個的loader去加載,有一個成功就不再往下嘗試,全失敗則封包件找不到。

    目前xLua除了原生的loader外,還添加了從Resource加載的loader,需要注意的是因為Resource隻支援有限的字尾,放Resources下的lua檔案得加上txt字尾(見附帶的例子)。

    建議的加載Lua腳本方式是:整個程式就一個DoString(“require ‘main’”),然後在main.lua加載其它腳本(類似lua腳本的指令行執行:lua main.lua)。

private LuaEnv luaenv;
    private void Start()
    {
        luaenv = new LuaEnv();
        luaenv.DoString("require 'helloWorld'");
        luaenv.Dispose();
    }
           
  • 簡單案例3 Loader方法
  1. 添加自定義的Loader方法
private void Start()
    {
        LuaEnv luaenv = new LuaEnv();
        luaenv.AddLoader(Add);//添加自定義Loader方法 傳遞參數是一個委托
        luaenv.DoString(" require 'helloWorld'");
        luaenv.Dispose();
    }
    //如果自定義的loader沒有傳回位元組數組 會繼續尋找系統内置的loader
    private byte[] Add(ref string filePath)//傳參為lua檔案路徑 
    {
        string s = "print('Add')";//s為lua腳本内容
        return System.Text.Encoding.UTF8.GetBytes(s);//将Text文本轉成位元組數組
    }
           
  1. 通過自定義路徑加載指定目錄的lua檔案
private void Start()
    {
        LuaEnv luaenv = new LuaEnv();
        luaenv.AddLoader(Add);//添加自定義Loader方法 傳遞參數是一個委托
        luaenv.DoString(" require 'add'");
        luaenv.Dispose();
    }
    //如果自定義的loader沒有傳回位元組數組 會繼續尋找系統内置的loader
    private byte[] Add(ref string filePath)//傳參為lua檔案路徑 
    {
        string path = Application.streamingAssetsPath + "/" + filePath + ".lua.txt";//得到lua檔案路徑
        string s= File.ReadAllText(path);//讀取txt檔案
        return System.Text.Encoding.UTF8.GetBytes(s);//将Text文本轉成位元組數組
    }
           
  • 簡單案例4 通過c#通路lua中的資料

    首先是以下案例中lua的代碼

    a=233;
    str="SKr"
    now=true;
    person={
    	name="小明",
    	age=10,
    think=function()
    	print("我從哪來?")
    end,
    	--有參數的方法的第一個參數務必加上self(變量名可以是合法的任意值,但一定要加上這個參數)
    --self參數代表目前表,在未設定該參數的情況下會作為隐藏參數存在 
    	add=function(arg,a,b) 
    	print(a+b)
    	end,
    	2,4,6,8,10
    }
    function person:add2(a,b) --通過:調用可以不用傳遞參數(),系統會自動傳遞目前的表
        print(a+b)
    end
    function person.add3(self,a,b) --通過.調用必須傳遞自身,self不會自動指派,我們必須通過第一個參數
        print(a+b)
    end
               

通過c#通路lua中的全局變量

private void Start()
    {
          LuaEnv luaenv = new LuaEnv();
        luaenv.DoString("require 'CSharpCallLua'");
     		//擷取lua中的全局變量
          int a = luaenv.Global.Get<int>("a");//num對應int float double三個類型
          Debug.Log(a);
          string str = luaenv.Global.Get<string>("SKr");
          Debug.Log(str);
          bool now = luaenv.Global.Get<bool>("now");
          Debug.Log(now);
        luaenv.Dispose();
      }
           

1.通過c#通路lua中的表(映射到普通class或struct)

​ 注:這種方式下xLua會幫你new一個執行個體,并把對應的字段指派過去。這個過程是值拷貝,如果class比較複雜代價會比較大。而且修改class的字段值不會同步到table,反過來也不會。

class Person
    {
          public string name;
        public int age;
      }
           

Start函數

Person person = luaenv.Global.Get<Person>("person");
  Debug.Log(person);
  Debug.Log(person.name);
  Debug.Log(person.age);
luaenv.Dispose();
           

2.通過c#通路lua中的表(映射到接口)

注!:如果改了接口後,之前生成的代碼出現錯誤,執行“Clear Generated Code”菜單!!!

接口

[CSharpCallLua] //lua映射到接口一定要加上這個特性
      interface IPerson
      {
          string name { get; set; }
          int age { get; set; }
          void think();
      }
           

Start函數

IPerson p = luaenv.Global.Get<IPerson>("person");
  Debug.Log(p.name);
  p.name = "小李";// 通過c#更改lua中的變量
  Debug.Log(p.name);
  p.think();//執行lua中的方法
  luaenv.Dispose();
           

lua方法也可以通過以下方式來定義

person={
      name="小明",
  	age=10
  }
  function person:add2(a,b) --通過:調用可以不用傳遞參數(),系統會自動傳遞目前的表
      print(a+b)
  end
  function person.add3(self,a,b) --通過.調用必須傳遞自身,self不會自動指派,我們必須通過第一個參數來傳遞目前的table
      print(a+b)
  end
           

3.通過c#通路lua中的表(映射到Dictionary或者List)

Start函數

...
  Dictionary<string, object> dic = luaenv.Global.Get<Dictionary<string, object>("person");
  foreach (var item in dic)
  {
   	print(item);//通過dic映射過來的值隻會映射有對應鍵的值
  }
  
  List<object> list = luaenv.Global.Get<List<object>>("person");
  foreach (var item in list)
  {
  	print(item);/通過list映射過來的值隻會映射沒有對應鍵的值
  }
  luaenv.Dispose();
  ...
           

4.通過c#通路lua中的表(映射到LuaTable類)

注:這種方式好處是不需要生成代碼,但也有一些問題,比如慢,比方式2要慢一個數量級,比如沒有類型檢查。一般情況下推薦使用第二種方式,少用第四種方式

Start函數

...
  LuaTable tab = luaenv.Global.Get<LuaTable>("person");
  print(tab.Get<string>("name"));
  print(tab.Get<int>("age"));
  luaenv.Dispose();
  ...
           

通過c#通路lua中的全局函數(映射到delegate)

lua

function fun1()
  	print("fun1!")
  end
  
  function fun2(a,b)
  	print(a+b)
  end
  
  function fun3(a,b)
  	return a+b
  end
           

c#

public class CSharpCallLuaTest : MonoBehaviour
  {
      LuaEnv luaenv = null;
      private void Start()
      {
          luaenv = new LuaEnv();
          luaenv.DoString("require 'CSharpCallLua'");
          //無參數委托
          Action fun1 = luaenv.Global.Get<Action>("fun1");
          fun1();
          fun1 = null;//删除c#對lua函數的引用使得lua虛拟機能順利釋放記憶體
          
  	      //傳參委托
          Fun2 fun2 = luaenv.Global.Get<Fun2>("fun2");
          fun2(2,4);
          fun2 = null;
  
          //傳參委托帶傳回值
          Fun3 fun3 = luaenv.Global.Get<Fun3>("fun3");
          int res=fun3(2, 4);
          print(res);
          fun3 = null;
  
          //傳參委托帶多個傳回值
          int resC;int resD;
          Fun4 fun4 = luaenv.Global.Get<Fun4>("fun4");
          //使用out來接受第一個傳回值以外的傳回值 ref同理但要注意賦初值
          int resAB = fun4(2, 4,out resC,out resD);
          print(resAB);
          print(resC);
          print(resD);
          fun4 = null;
  
          luaenv.Dispose();
      }
      
      [CSharpCallLua] //委托必須加上CSharpCallLua特性
      delegate void Fun2(int a, int b);
      [CSharpCallLua]
      delegate int Fun3(int a, int b);
      [CSharpCallLua]
      delegate int Fun4(int a, int b,out int c,out int d);
           

通過c#通路lua中的全局函數(映射到LuaFunction)

lua代碼見上文

c#

//通過LuaFunction映射lua方法比較耗費性能 一般不推薦使用
LuaFunction luaFun = luaenv.Global.Get<LuaFunction>("fun4");
object[] calls= luaFun.Call(2, 5);
foreach (var item in calls)
{
    print(item);
}
luaenv.Dispose();
           

xLua作者的使用建議:

1、通路lua全局資料,特别是table以及function,代價比較大,建議盡量少做,比如在初始化時把要調用的lua function擷取一次(映射到delegate)後,儲存下來,後續直接調用該delegate即可。table也類似。

2、如果lua測的實作的部分都以delegate和interface的方式提供,使用方可以完全和xLua解耦:由一個專門的子產品負責xlua的初始化以及delegate、interface的映射,然後把這些delegate和interface設定到要用到它們的地方。

  • 簡單案例5 通過lua通路c#中的資料

lua

--需要經常通路的類,可以先用局部變量引用後通路
--所有C#相關的都放到CS下即以CS開頭
local gameObject=CS.UnityEngine.GameObject
--lua裡頭沒有new關鍵字 可以擁有這種方式建立一個GameObject對象
gameObject("new") 
--通過lua代碼改變Unity中的相機名字
local camera=gameObject.Find("Main Camera")
--修改camera對象的名字
camera.name="ChangeName"
--得到camera對象的Camera元件 使用:會把目前的對象當作第一個參數傳遞過去
--建議所有的成員方法都通過:來調用
--local cameraCom=camera.GetComponent(camera,"Camera")//用.調用需要傳遞目前對象
local cameraCom=camera:GetComponent("Camera")
--摧毀相機元件
gameObject.Destroy(cameraCom)
           

c#

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

        luaenv.Dispose();
    }