C#源代碼->dll->安裝包
Lua源代碼->安裝包
Lua源代碼可以在客戶端直接下載沒有編譯的過程
1.直接用Lua調(diào)用C#的方法
使用XLua輸出HelloWorld
引入命名空間XLua,聲明一個(gè)LuaEnv的類的對象,這個(gè)類提供了一些方法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
public class HellowWorld01 : MonoBehaviour {
private LuaEnv luaenv;
void Start () {
luaenv = new LuaEnv();
luaenv.DoString("print('hello world')");
//luaenv.DoString("CS.UnityEngine.Debug.Log('hello kk')");
}
private void OnDestroy()
{
luaenv.Dispose();
}
}
輸出結(jié)果:
LUA: hello world
2.通過讀取Lua文件執(zhí)行Lua語句
//加載helloWorld.lua.txt,TextAsset會(huì)自動(dòng)加txt的后綴
TextAsset ta = Resources.Load<TextAsset>("helloWorld.lua");
//print(ta);
LuaEnv luaEnv = new LuaEnv();
luaEnv.DoString(ta.text);
luaEnv.Dispose();
3.自定義Loader
在xLua加自定義loader是很簡單的志珍,只涉及到一個(gè)接口:
public delegate byte[] CustomLoader(ref string filepath);
public void LuaEnv.AddLoader(CustomLoader loader)
通過AddLoader可以注冊個(gè)回調(diào)牡直,該回調(diào)參數(shù)是字符串清寇,lua代碼里頭調(diào)用require時(shí)礁芦,參數(shù)將會(huì)透傳給回調(diào),回調(diào)中就可以根據(jù)這個(gè)參數(shù)去加載指定文件坡垫,如果需要支持調(diào)試彭沼,需要把filepath修改為真實(shí)路徑傳出缔逛。該回調(diào)返回值是一個(gè)byte數(shù)組,如果為空表示該loader找不到姓惑,否則則為lua文件的內(nèi)容褐奴。
public class CreateLoader : MonoBehaviour {
private LuaEnv luaEnv;
// Use this for initialization
void Awake () {
luaEnv = new LuaEnv();
luaEnv.AddLoader(MyLoader);
luaEnv.DoString("require 'helloWorld.lua'");
luaEnv.Dispose();
}
private byte[] MyLoader(ref string filePath)
{
string s = "print('best kk')";
return System.Text.Encoding.UTF8.GetBytes(s);
}
}
4.讀取非Resources文件夾下的lua文件通過自定義Loader
public class CreateLoader : MonoBehaviour {
private LuaEnv luaEnv;
void Awake () {
luaEnv = new LuaEnv();
luaEnv.AddLoader(MyLoader);
luaEnv.DoString("require 'test007'");
luaEnv.Dispose();
}
private byte[] MyLoader(ref string filePath)
{
print(filePath);
print(Application.streamingAssetsPath);
string path = Application.streamingAssetsPath + "/" + filePath + ".lua.txt";
return System.Text.Encoding.UTF8.GetBytes(File.ReadAllText(path));
}
}
C#訪問Lua
一.獲取Lua中的全局變量
int a = luaEnv.Global.Get<int>("a");
print(a);
二.訪問一個(gè)全局的Table
在C#中定義一個(gè)類與Lua中的Table相對應(yīng)
class Person
{
public string name;
public int age;
}
在用這個(gè)類創(chuàng)建對象來進(jìn)行接收
Person kk = luaEnv.Global.Get<Person>("person");
print(kk.name + " " + kk.age);
2.使用接口訪問Table
聲明一個(gè)接口,添加CSharpCallLua的特性
[CSharpCallLua]
interface IPerson
{
string name { get; set; }
int age { get; set; }
}
使用接口將獲取Lua文件中的引用于毙,這與通過聲明類不同敦冬,修改實(shí)例化的接口將直接修改Lua中的數(shù)據(jù)。
在接口中聲明方法將直接對應(yīng)Lua中Table中的方法唯沮。
person = {
name = "kk";
age = 23;
eat = function()
print("111");
end
}
[CSharpCallLua]
interface IPerson
{
string name { get; set; }
int age { get; set; }
void eat();
}
3.使用輕量級的映射方式字典或者集合
將Lua中Table的鍵值對存在Dictionary中脖旱,存在限制,沒有明確的鍵的無法存在Dictionary中介蛉。
Dictionary<string, object> dic = luaEnv.Global.Get<Dictionary<string,object>>("person");
foreach (string item in dic.Keys)
{
print(item + " " + dic[item]);
}
將Lua中Table的鍵值對存在List中萌庆,List中只能存沒有明確鍵的。只能映射值币旧,沒有key的践险。
List<object> list = luaEnv.Global.Get<List<object>>("person");
foreach (var item in list)
{
print(item);
}
4.另一種by ref方式:映射到LuaTable類
LuaTable luaTable = luaEnv.Global.Get<LuaTable>("person");
print(luaTable.Get<string>("name"));
print(luaTable.Get<string>("age"));
在LuaTable中可以通過.GetInPath獲得表中表
person = {
man = {height = 100;};
name = "kk";
age = 23,
2,
3,
4,
56,
eat = function()
print("111");
end
}
print(luaTable.GetInPath<int>("man.height"));
三.訪問一個(gè)全局的function
1.映射到delegate
在C#中delegate的參數(shù)與Lua中的funciton的參數(shù)對應(yīng)即可
Action act1 = luaEnv.Global.Get<Action>("add");
act1();
act1 = null;//釋放對Lua的引用
在Lua中的方法需要傳參的時(shí)候:
function add(a,b)
print(a+b);
end
在定義委托的時(shí)候就用delegate并添加CSharpCallLua的特性
[CSharpCallLua]
delegate void Add(int a, int b);
接受返回值:
function add(a,b)
print(a+b);
return 666;
end
[CSharpCallLua]
delegate int Add(int a, int b);
多返回值
使用ref或out參數(shù)接受多的返回值
function add(a,b)
print(a+b);
return 666,a,b;
end
[CSharpCallLua]
delegate int Add(int a, int b,out int resa,out int resb);
Add add = luaEnv.Global.Get<Add>("add");
//print (add(3, 2));
int resa;
int resb;
int ans = add(6,9,out resa,out resb);
print(resa + "__" + resb);
2.映射到LuaFunction
調(diào)用起來比較慢,LuaFunction上有個(gè)變參Call函數(shù),可以傳任意類型捏境,任意個(gè)數(shù)的參數(shù)于游,返回值是object的數(shù)組毁葱,對應(yīng)于lua的多返回值垫言。
四.使用建議
C#調(diào)用Lua全局的變量代價(jià)較大盡量少做,可以的話可以講Lua中的變量存起來避免重復(fù)的映射倾剿。適用方于xLua解耦筷频。
Lua訪問C#
newC#對象
在Lua中創(chuàng)建一個(gè)游戲物體:
CS.UnityEngine.GameObject()
lua中沒有new關(guān)鍵字。
所有C#相關(guān)都放到CS下前痘,包括構(gòu)造函數(shù)凛捏、靜態(tài)成員屬性、方法
xlua支持重載
訪問C#靜態(tài)屬性芹缔、方法
Lua中調(diào)用某一個(gè)對象中的方法要通過:的方式進(jìn)行調(diào)用坯癣。
new C#對象
你在C#這樣new一個(gè)對象:
var newGameObj = new UnityEngine.GameObject();
對應(yīng)到Lua是這樣:
local newGameObj = CS.UnityEngine.GameObject()
基本類似,除了:
1最欠、lua里頭沒有new關(guān)鍵字示罗;
2、所有C#相關(guān)的都放到CS下芝硬,包括構(gòu)造函數(shù)蚜点,靜態(tài)成員屬性、方法拌阴;
如果有多個(gè)構(gòu)造函數(shù)呢绍绘?放心,xlua支持重載迟赃,比如你要調(diào)用GameObject的帶一個(gè)string參數(shù)的構(gòu)造函數(shù)陪拘,這么寫:
local newGameObj2 = CS.UnityEngine.GameObject('helloworld')
訪問C#靜態(tài)屬性,方法
讀靜態(tài)屬性
CS.UnityEngine.Time.deltaTime
寫靜態(tài)屬性
CS.UnityEngine.Time.timeScale = 0.5
調(diào)用靜態(tài)方法
CS.UnityEngine.GameObject.Find('helloworld')
小技巧:如果需要經(jīng)常訪問的類纤壁,可以先用局部變量引用后訪問藻丢,除了減少敲代碼的時(shí)間,還能提高性能:
local GameObject = CS.UnityEngine.GameObject
GameObject.Find('helloworld')
訪問C#成員屬性摄乒,方法
讀成員屬性
testobj.DMF
寫成員屬性
testobj.DMF = 1024
調(diào)用成員方法
注意:調(diào)用成員方法悠反,第一個(gè)參數(shù)需要傳該對象,建議用冒號(hào)語法糖馍佑,如下
testobj:DMFunc()
父類屬性斋否,方法
xlua支持(通過派生類)訪問基類的靜態(tài)屬性,靜態(tài)方法拭荤,(通過派生類實(shí)例)訪問基類的成員屬性茵臭,成員方法
參數(shù)的輸入輸出屬性(out,ref)
Lua調(diào)用測的參數(shù)處理規(guī)則:C#的普通參數(shù)算一個(gè)輸入形參舅世,ref修飾的算一個(gè)輸入形參旦委,out不算奇徒,然后從左往右對應(yīng)lua 調(diào)用測的實(shí)參列表;
Lua調(diào)用測的返回值處理規(guī)則:C#函數(shù)的返回值(如果有的話)算一個(gè)返回值缨硝,out算一個(gè)返回值摩钙,ref算一個(gè)返回值,然后從左往右對應(yīng)lua的多返回值查辩。
重載方法
直接通過不同的參數(shù)類型進(jìn)行重載函數(shù)的訪問胖笛,例如:
testobj:TestFunc(100)
testobj:TestFunc('hello')
將分別訪問整數(shù)參數(shù)的TestFunc和字符串參數(shù)的TestFunc。
注意:xlua只一定程度上支持重載函數(shù)的調(diào)用宜岛,因?yàn)閘ua的類型遠(yuǎn)遠(yuǎn)不如C#豐富长踊,存在一對多的情況,比如C#的int萍倡,float身弊,double都對應(yīng)于lua的number,上面的例子中TestFunc如果有這些重載參數(shù)列敲,第一行將無法區(qū)分開來阱佛,只能調(diào)用到其中一個(gè)(生成代碼中排前面的那個(gè))
操作符
支持的操作符有:+,-酿炸,*瘫絮,/,==填硕,一元-麦萤,<,<=扁眯, %壮莹,[]
參數(shù)帶默認(rèn)值的方法
和C#調(diào)用有默認(rèn)值參數(shù)的函數(shù)一樣,如果所給的實(shí)參少于形參姻檀,則會(huì)用默認(rèn)值補(bǔ)上命满。
可變參數(shù)方法
對于C#的如下方法:
void VariableParamsFunc(int a, params string[] strs)
可以在lua里頭這樣調(diào)用:
testobj:VariableParamsFunc(5, 'hello', 'john')
使用Extension methods
在C#里定義了,lua里就能直接使用绣版。
泛化(模版)方法
不直接支持胶台,可以通過Extension methods功能進(jìn)行封裝后調(diào)用。
枚舉類型
枚舉值就像枚舉類型下的靜態(tài)屬性一樣杂抽。
testobj:EnumTestFunc(CS.Tutorial.TestEnum.E1)
上面的EnumTestFunc函數(shù)參數(shù)是Tutorial.TestEnum類型的
另外诈唬,如果枚舉類加入到生成代碼的話,枚舉類將支持__CastFrom方法缩麸,可以實(shí)現(xiàn)從一個(gè)整數(shù)或者字符串到枚舉值的轉(zhuǎn)換铸磅,例如:
CS.Tutorial.TestEnum.__CastFrom(1)
CS.Tutorial.TestEnum.__CastFrom('E1')
delegate使用(調(diào)用,+,-)
C#的delegate調(diào)用:和調(diào)用普通lua函數(shù)一樣
+操作符:對應(yīng)C#的+操作符阅仔,把兩個(gè)調(diào)用串成一個(gè)調(diào)用鏈吹散,右操作數(shù)可以是同類型的C# delegate或者是lua函數(shù)。
-操作符:和+相反八酒,把一個(gè)delegate從調(diào)用鏈中移除空民。
Ps:delegate屬性可以用一個(gè)luafunction來賦值。
event
比如testobj里頭有個(gè)事件定義是這樣:public event Action TestEvent;
增加事件回調(diào)
testobj:TestEvent('+', lua_event_callback)
移除事件回調(diào)
testobj:TestEvent('-', lua_event_callback)
64位整數(shù)支持
Lua53版本64位整數(shù)(long丘跌,ulong)映射到原生的64未整數(shù)袭景,而luaji版本t唁桩,相當(dāng)于lua5.1的標(biāo)準(zhǔn)闭树,本身不支持64位,xlua做了個(gè)64位支持的擴(kuò)展庫荒澡,C#的long和ulong都將映射到userdata:
1报辱、支持在lua里頭進(jìn)行64位的運(yùn)算,比較单山,打印
2碍现、支持和lua number的運(yùn)算,比較
3米奸、要注意的是昼接,在64擴(kuò)展庫中,實(shí)際上只有int64悴晰,ulong也會(huì)先強(qiáng)轉(zhuǎn)成long再傳遞到lua慢睡,而對ulong的一些運(yùn)算,比較铡溪,我們采取和java一樣的支持方式漂辐,提供一組API,詳情請看API文檔棕硫。
C#復(fù)雜類型和table的自動(dòng)轉(zhuǎn)換
對于一個(gè)有無參構(gòu)造函數(shù)的C#復(fù)雜類型髓涯,在lua側(cè)可以直接用一個(gè)table來代替,該table對應(yīng)復(fù)雜類型的public字段有相應(yīng)字段即可哈扮,支持函數(shù)參數(shù)傳遞纬纪,屬性賦值等,例如:
C#下B結(jié)構(gòu)體(class也支持)定義如下:
public struct A
{
public int a;
}
public struct B
{
public A b;
public double c;
}
某個(gè)類有成員函數(shù)如下:
void Foo(B b)
在lua可以這么調(diào)用
obj:Foo({b = {a = 100}, c = 200})
獲取類型(相當(dāng)于C#的typeof)
比如要獲取UnityEngine.ParticleSystem類的Type信息滑肉,可以這樣
typeof(CS.UnityEngine.ParticleSystem)
“強(qiáng)”轉(zhuǎn)
lua沒類型包各,所以不會(huì)有強(qiáng)類型語言的“強(qiáng)轉(zhuǎn)”,但有個(gè)有點(diǎn)像的東西:告訴xlua要用指定的生成代碼去調(diào)用一個(gè)對象赦邻,這在什么情況下能用到呢髓棋?有的時(shí)候第三方庫對外暴露的是一個(gè)interface或者抽象類,實(shí)現(xiàn)類是隱藏的,這樣我們無法對實(shí)現(xiàn)類進(jìn)行代碼生成按声。該實(shí)現(xiàn)類將會(huì)被xlua識(shí)別為未生成代碼而用反射來訪問膳犹,如果這個(gè)調(diào)用是很頻繁的話還是很影響性能的,這時(shí)我們就可以把這個(gè)interface或者抽象類加到生成代碼签则,然后指定用該生成代碼來訪問:
cast(calc, typeof(CS.Tutorial.Calc))
上面就是指定用CS.Tutorial.Calc的生成代碼來訪問calc對象须床。
EG:在VSCode中找到files.associations文件,在settings.json中添加
{
"files.associations": {"*.lua.txt":"lua"}
}
就可以在txt文件中編寫lua代碼并顯示高亮了