柚子快報(bào)激活碼778899分享:游戲 設(shè)計(jì)模式 xLua詳解
柚子快報(bào)激活碼778899分享:游戲 設(shè)計(jì)模式 xLua詳解
目錄
環(huán)境準(zhǔn)備xLua導(dǎo)入
C#調(diào)用LuaLua解析器Lua文件加載重定向Lua解析管理器全局變量的獲取全局函數(shù)的獲取List和Dictionary映射table類映射table接口映射tableLuaTable映射table
Lua調(diào)用C#準(zhǔn)備工作Lua使用C#類Lua調(diào)用C#枚舉Lua使用C# 數(shù)組 List 字典數(shù)組List字典
Lua使用C#擴(kuò)展方法Lua使用C# ref和out函數(shù)refout
函數(shù)重載lua調(diào)用C# 委托和事件Lua使用C#二維數(shù)組Lua中null與nil的比較系統(tǒng)類型與Lua互相訪問lua使用C#協(xié)程Lua使用C#泛型
環(huán)境準(zhǔn)備
xLua導(dǎo)入
我們來到github搜索xLua,直接下載zip壓縮包 我們把這兩個(gè)文件夾復(fù)制到工程中 編譯完之后窗口上就會(huì)有這個(gè)Xlua的選項(xiàng)(這里有可能會(huì)提示某個(gè)腳本編譯失敗,加個(gè)using System.Reflection) 我們可以先點(diǎn)擊第二個(gè)選項(xiàng),再點(diǎn)擊第一個(gè)選項(xiàng)生成 然后這里要導(dǎo)入ABbrowser工具,詳情請(qǐng)看AssetBundle詳解 這里根據(jù)教程還需要導(dǎo)入三個(gè)類 我依次寫到這上面 BaseManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BaseManager
{
private static T instance;
public static T GetInstance()
{
if (instance == null)
instance = new T();
return instance;
}
}
SingletonAutoMono
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SingletonAutoMono
{
private static T instance;
public static T GetInstance()
{
if (instance == null)
{
GameObject obj = new GameObject();
obj.name = typeof(T).ToString();
DontDestoryOnLoad(obj);
instance = obj.AddComponent
}
return instance;
}
}
SingletonMono類
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// Start is called before the first frame update
public class SingletonMono
{
private static T instance;
public static T Getinstance()
{
retrun instance;
}
protected virtual void Awake()
{
instance = this;
}
}
然后我們要導(dǎo)入AB包管理器,這個(gè)也在AssetBundle詳解里
C#調(diào)用Lua
Lua解析器
這里的核心就是LuaEnv,我們把腳本附著到相機(jī)上,然后運(yùn)行
public class Lesson1_LuaEnv : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
//Lua解析器 能夠讓我們?cè)赨nity中執(zhí)行Lua
LuaEnv env = new LuaEnv();
//執(zhí)行Lua語言
env.DoString("print('こにちは')");
}
// Update is called once per frame
void Update()
{
}
}
//幫助我們定時(shí)清除Lua中沒有手動(dòng)釋放的對(duì)象
//在幀更新中定時(shí)執(zhí)行或切場(chǎng)景時(shí)執(zhí)行
env.Tick();
//銷毀Lua解析器
env.Dispose();
這里如果有很多個(gè)語句需要執(zhí)行,一句一句調(diào)用太慢了怎么辦,我們這里可以使用DoString通過字符名來執(zhí)行Lua腳本名,Lua中使用require來跨腳本運(yùn)行 我們現(xiàn)在先在Unity中新建一個(gè)Resources文件夾, 然后在文件夾中新建一個(gè).txt文件再把后綴更改為.lua 再用相應(yīng)的lua軟件打開 我們先只寫一句話 但是現(xiàn)在又有個(gè)問題,Unity無法直接識(shí)別Lua文件,所以再改成Main.lua.txt
env.DoString("require('Main')");
Lua文件加載重定向
這部分的內(nèi)容是自定義lua文件加載路徑 當(dāng)require被調(diào)用時(shí),會(huì)先去Addloader中的函數(shù)路徑找文件,然后再去默認(rèn)路徑(Resources)中尋找 我們現(xiàn)在寫一個(gè)基本邏輯,Addloder會(huì)實(shí)現(xiàn)重定向功能,它接受一個(gè)委托
void Start()
{
LuaEnv env = new LuaEnv();
env.AddLoader(MyCustomLoader);
env.DoString("require('Main')");
}
//自動(dòng)執(zhí)行
private byte[] MyCustomLoader(ref string filePath)
{
Debug.Log(filePath);
//通過函數(shù)中的邏輯,去加載lua文件
return null;
}
我們新建一個(gè)叫做Lua的文件夾 然后在里面新建一個(gè)Main文件 我們來詳細(xì)解釋一下這段代碼 這里AddLoader的調(diào)用參數(shù)是一個(gè)委托變量,類似于C++中的函數(shù)指針,而MyCustomLoader這里是通過env傳入的lua文件名。如果我把 env.DoString(“require(‘Main’)”);這行注釋掉,那么MyCustomLoader將不會(huì)執(zhí)行 在這里,filePath是Main,Application.dataPath是Unity/xxx/Asset
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
using System.IO;
using Unity.VisualScripting;
public class Lesson2_Loader : MonoBehaviour
{
void Start()
{
LuaEnv env = new LuaEnv();
env.AddLoader(MyCustomLoader);
env.DoString("require('Main')");
}
//自動(dòng)執(zhí)行
private byte[] MyCustomLoader(ref string filePath)
{
//傳入的參數(shù)是require執(zhí)行的腳本文件名
string path = Application.dataPath + "/Lua/" + filePath + ".lua";
Debug.Log(path);
if(File.Exists(path))
{
return(File.ReadAllBytes(path));
}
else
{
Debug.Log("重定向失敗,文件名為" + filePath);
}
//拼接一個(gè)lua所在路徑
//通過函數(shù)中的邏輯,去加載lua文件
return null;
}
}
Lua解析管理器
我們用一個(gè)管理器來管理LuaEnv 其他邏輯都是前面提到過的
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
using System.IO;
//Lua管理器
//提供lua解析器
//保證解析器的唯一性
public class LuaMgr:BaseManager
{
//執(zhí)行Lua語言的函數(shù)
//釋放垃圾
//銷毀
//重定向
private LuaEnv luaEnv;
public void Init()
{
//如果已經(jīng)初始化了,直接返回
if (luaEnv != null)
return;
luaEnv = new LuaEnv();
//加載lua腳本重定向
luaEnv.AddLoader(MyCustomLoader);
}
private byte[] MyCustomLoader(ref string filePath)
{
string path = Application.dataPath + "/Lua/" + filePath + ".lua";
Debug.Log(path);
if (File.Exists(path))
{
return (File.ReadAllBytes(path));
}
else
{
Debug.Log("重定向失敗,文件名為" + filePath);
}
return null;
}
public void DoString(string str)
{
luaEnv.DoString(str);
}
public void Tick()
{
luaEnv.Tick();
}
public void Dispose()
{
luaEnv.Dispose();
luaEnv = null;
}
}
然后我們寫一個(gè)測(cè)試類來測(cè)試 我們的代碼邏輯在這個(gè)腳本中第一次使用LuaMgr,所以需要Init,如果初始化之后在其他的腳本就可以直接使用LuaMgr了
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson3_LuaMgr : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
LuaMgr.GetInstance().Init();//LuaMgr是一個(gè)單例類,所以可以直接訪問
LuaMgr.GetInstance().DoString("require('Main')");
}
// Update is called once per frame
void Update()
{
}
}
接下來我們講AB包和Mgr相關(guān)邏輯 由于AB包不識(shí)別lua,所以還是先修改成txt 我們選中Main文件,新建一個(gè)lua的AB包 我們先清除一下代碼 然后構(gòu)建 現(xiàn)在我們要做的其實(shí)就是在AB包中執(zhí)行l(wèi)ua 我們?cè)賮斫忉尯瘮?shù)邏輯 這里我們又加了一個(gè)重定向邏輯MyCustomABLoader,用來從AB包中加載數(shù)據(jù),在MyCustomABLoader中,我們之前使用的邏輯是手動(dòng)聲明AB包的對(duì)象然后進(jìn)行讀取,但是我們?cè)贏B包的教程中做了一個(gè)AB包管理器,所以這里使用AB包管理器來做
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
using System.IO;
//Lua管理器
//提供lua解析器
//保證解析器的唯一性
public class LuaMgr:BaseManager
{
//執(zhí)行Lua語言的函數(shù)
//釋放垃圾
//銷毀
//重定向
private LuaEnv luaEnv;
public void Init()
{
//如果已經(jīng)初始化了,直接返回
if (luaEnv != null)
return;
luaEnv = new LuaEnv();
//加載lua腳本重定向
luaEnv.AddLoader(MyCustomLoader);
luaEnv.AddLoader(MyCustomABLoader);
}
public void DoLuaFIle(string fileName)
{
string str = string.Format("require('{0}')",fileName);
}
private byte[] MyCustomLoader(ref string filePath)
{
string path = Application.dataPath + "/Lua/" + filePath + ".lua";
Debug.Log(path);
if (File.Exists(path))
{
return (File.ReadAllBytes(path));
}
else
{
Debug.Log("重定向失敗,文件名為" + filePath);
}
return null;
}
//重定向加載AB包中的lua腳本
private byte[] MyCustomABLoader(ref string filePath)
{
//從AB包中加載lua文件
//加載AB包
// string path = Application.streamingAssetsPath + "/lua";
// AssetBundle ab = AssetBundle.LoadFromFile(path);
// TextAsset tx = ab.LoadAsset
// //加載除了lua文件 byte數(shù)組
return tx.bytes;
TextAsset lua=ABMgr.GetInstance().LoadRes
if (lua != null)
return lua.bytes;
else
return null;
}
public void DoString(string str)
{
luaEnv.DoString(str);
}
public void Tick()
{
luaEnv.Tick();
}
public void Dispose()
{
luaEnv.Dispose();
luaEnv = null;
}
}
全局變量的獲取
我們?cè)贛ain.lua的文件下新建一個(gè)Test.lua 在test中定義如下變量 這里我們?cè)L問全局變量的方式是通過Main.lua進(jìn)行的 這里很有意思的是,require調(diào)用Test的邏輯本質(zhì)上并不是Main自己調(diào)用的,而是xlua調(diào)用的,通過堆??梢钥吹?所以如果我require(‘一個(gè)不存在的文件’),會(huì)在xlua層報(bào)錯(cuò)也是有跡可循的了。
現(xiàn)在我們要使用mgr來調(diào)用Test中的變量 這個(gè)Globe有點(diǎn)類似大G表,可以通過這個(gè)操縱Test中的數(shù)據(jù)
int i=LuaMgr.GetInstance().Global.Get
Debug.Log(i);
可以發(fā)現(xiàn)成功打印 但是這里取到的值也只是個(gè)拷貝,并不會(huì)修改原來的數(shù) 如果想修改可以使用set 這里也不能取到test中聲明的本地變量,換言之只能操作大G表的內(nèi)容
全局函數(shù)的獲取
函數(shù)我們依舊在test中寫 我們先調(diào)用無參無返回值方法,使用委托
public class Lesson5_CallFunction : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
//無參無返回的獲取
//委托
CustomCall call = LuaMgr.GetInstance().Global.Get
call();
}
// Update is called once per frame
void Update()
{
}
}
有參有返回:
public delegate void CustomCall2(int a);
//調(diào)用
[CSharpCallLua]//自定義委托一定要有
CustomCall2 call2 = LuaMgr.GetInstance().Global.Get
call2(10);
這里可能會(huì)報(bào)錯(cuò),去xlua窗口重新生成一下就好了 這里委托都可以使用Unity/C#/提供的,不需要自己寫 接下來是多返回值,在C#中使用out 和 ref來接收 首先是聲明委托
//多返回值委托
[CSharpCallLua]
public delegate int CustomCall3(int a, out int b, out bool c, out string d, out int e);
然后是接收
//多返回值獲取
CustomCall3 call3 = LuaMgr.GetInstance().Global.Get
int b;bool c; string d; int e;
Debug.Log("多返回值:" + call3(100, out b, out c, out d, out e));
Debug.Log(b + "_" + c + "_" + d + "_" + e);
然后是變長(zhǎng)參數(shù) 首先說一下ref和out的區(qū)別 ref 關(guān)鍵字用于傳遞一個(gè)已初始化的變量作為參數(shù),并且要求方法在使用這個(gè)參數(shù)之前初始化它。 out 關(guān)鍵字用于傳遞一個(gè)未初始化的變量作為參數(shù),方法在使用這個(gè)參數(shù)之前必須將其初始化。 我們依舊可以用這些參數(shù)
[CSharpCallLua]
public delegate int CustomCall4(int a, ref int b, ref bool c, ref string d, ref int e);
//多返回值獲取
CustomCall4 call4 = LuaMgr.GetInstance().Global.Get
int b1=0; bool c1=false; string d1=""; int e1=0;//要初始化
Debug.Log("變長(zhǎng)參數(shù):" + call4(100, ref b, ref c, ref d, ref e));
Debug.Log(b + "_" + c + "_" + d + "_" + e);
以下是完整代碼
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using XLua;
//無參無返回值的委托
public delegate void CustomCall();
//有參有返回的委托
[CSharpCallLua]
public delegate void CustomCall2(int a);
//多返回值委托
[CSharpCallLua]
public delegate int CustomCall3(int a, out int b, out bool c, out string d, out int e);
//變長(zhǎng)參數(shù)
[CSharpCallLua]
public delegate int CustomCall4(int a, ref int b, ref bool c, ref string d, ref int e);
public class Lesson5_CallFunction : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
//無參無返回的獲取
//委托
CustomCall call = LuaMgr.GetInstance().Global.Get
call();
//有參有返回的獲取
CustomCall2 call2 = LuaMgr.GetInstance().Global.Get
call2(10);
//多返回值獲取
CustomCall3 call3 = LuaMgr.GetInstance().Global.Get
int b;bool c; string d; int e;
Debug.Log("多返回值:" + call3(100, out b, out c, out d, out e));
Debug.Log(b + "_" + c + "_" + d + "_" + e);
//多返回值獲取
CustomCall4 call4 = LuaMgr.GetInstance().Global.Get
int b1=0; bool c1=false; string d1=""; int e1=0;
Debug.Log("變長(zhǎng)參數(shù):" + call4(100, ref b, ref c, ref d, ref e));
Debug.Log(b + "_" + c + "_" + d + "_" + e);
}
// Update is called once per frame
void Update()
{
}
}
List和Dictionary映射table
我們先在test中聲明
testList={1,2,3,4,5,6}
testList2={"123","123",true,1,1.2}
--Dictionary
testDic1={
["1"]=1,
["2"]=1,
["3"]=1,
["4"]=1
}
testDic2={
["1"]=1,
[true]=1,
[false]=true
["123"]=false
}
老方法調(diào)用
List>("testList");
for(int i = 0; i < list.Count; i++)
{
Debug.Log(list[i]);
}
不過get還是不能改數(shù)據(jù)本身 我們?cè)倏匆幌聞偛诺膖estDic,testList2存的不只有int型 所以我們?cè)谌〉街档臅r(shí)候選擇object
List
for (int i = 0; i < list2.Count; i++)
{
Debug.Log(list2[i]);
}
Dictionary
foreach(string i in dic.Keys)
{
Debug.Log(i + "_" + dic[i]);
}
至于testDic2,我們可以都用dictory
類映射table
在這里我們要實(shí)現(xiàn)類邏輯 首先在test中寫一個(gè)類邏輯
testClass={
testInt=2,
testBool=true,
testString="123",
testFun=function()
print("123123123")
end
}
然后在腳本中聲明一個(gè)名字一樣的類
public class CallLuaClass
{
//在這個(gè)類中聲明成員變量
//名字一定要和Lua那邊的一樣
public int testInt;
public bool testBool;
public float testFloat;
public string testString;
public UnityAction testFun;
//這個(gè)自定義中的變量可以更多也可以更少
//如果比lua中的多少都會(huì)忽略
}
然后在腳本中把這個(gè)類”實(shí)例化“出來
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
CallLuaClass obj = LuaMgr.GetInstance().Global.Get
Debug.Log(obj.testBool);
Debug.Log(obj.testInt);
Debug.Log(obj.testFun);
和之前一樣,這個(gè)是值拷貝而不是引用拷貝 然后我們?cè)賮砜匆幌虑短子成?,我們?cè)谶@個(gè)類里再加一個(gè)
testClass={
testInt=2,
testBool=true,
testString="123",
testFun=function()
print("123123123")
end
testClass2={
testInt2=3;
}
}
然后在聲明處
public class CallLuaClass2
{
public int testInt2;
}
void Start()
{
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
CallLuaClass obj = LuaMgr.GetInstance().Global.Get
// Debug.Log(obj.testClass2.testInt2);
CallLuaClass2 obj2 = obj.testClass2;
Debug.Log(obj2.testInt2);
}
接口映射table
先定義一個(gè)接口
[CSharpCallLua]//這里也要加
public interface ICSharpCallInterface
{
//接口中是不允許有成員變量的
//用屬性來接收
public int testInt
{
get;
set;
}
public bool testBool
{
get;
set;
}
UnityAction testFun
{
get;
set;
}
}
ICSharpCallInterface obj = LuaMgr.GetInstance().Global.Get
obj.testFun();
但是這里唯一不同的是,接口拷貝是引用拷貝,lua表中的值也變了
LuaTable映射table
我們經(jīng)常使用的Globe本質(zhì)上就是一個(gè)luaTable 所以想訪問一個(gè)屬性很簡(jiǎn)單
LuaTable table= LuaMgr.GetInstance().Global.Get
ebug.Log(table.Get
table.Get
不建議使用LuaTable和luaFunction 效率比較低
用完之后一定要記住table.Dispose銷毀
Lua調(diào)用C#
準(zhǔn)備工作
我們先來新建一個(gè)文件夾,專門存Lua調(diào)用C#的代碼 然后新建一個(gè)Main腳本 Lua沒有辦法直接訪問C#,一定是先從C#調(diào)用Lua腳本后才把核心邏輯交給Lua來編寫 先做準(zhǔn)備工作
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Main : MonoBehaviour
{
void Start()
{
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
}
}
把Main腳本放到攝像機(jī)上
我們新建一個(gè)lua 我們?cè)贛ain中調(diào)用新建的lua
Lua使用C#類
Lua使用C#的類非常簡(jiǎn)單
CS.命名空間.類名 先看Main腳本
--Main腳本
print("主Lua腳本已啟動(dòng)")
require("Lesson1_CallClass")
然后是Lesson1_CallClass腳本
Lua中沒有new 所以我們類名括號(hào)就是實(shí)例化對(duì)象
--Lesson1_CallClass
print("lua調(diào)用C#類相關(guān)知識(shí)點(diǎn)")
local obj1=CS.UnityEngine.GameObject()
我們來梳理一下函數(shù)邏輯
首先我們?cè)贑#中調(diào)用初始化函數(shù):先創(chuàng)建唯一實(shí)例LuaEnv,然后使用重定向(找到lua文件的位置)通過LuaEnv的DoString方法執(zhí)行l(wèi)ua邏輯現(xiàn)在我們到了lua腳本中,由于之前我們已經(jīng)聲明將調(diào)用lua中的Main文件,所以來到Main文件,這里執(zhí)行打印語句,然后執(zhí)行require語句,轉(zhuǎn)到Lesson1_CallClass中來到Lesson1_CallClass中,先打印,然后在場(chǎng)景中創(chuàng)建一個(gè)空GameObject
結(jié)果:
現(xiàn)在我使用CS.UnityEngine.GameObject()相當(dāng)于調(diào)用一個(gè)無參函數(shù),我也可以在括號(hào)里面?zhèn)魅朊肿鳛橐粋€(gè)有參函數(shù)
如果是類對(duì)象中的成員方法,要加冒號(hào)
這里我們使用帶參構(gòu)造函數(shù),并且使用Gameobject作為別名,我們通過obj3.transform再加冒號(hào)調(diào)用translate
print("lua調(diào)用C#類相關(guān)知識(shí)點(diǎn)")
local obj2=CS.UnityEngine.GameObject("Theshy")
GameObject=CS.UnityEngine.GameObject
local obj3=GameObject.Find("Theshy")
obj3.transform:Translate(CS.UnityEngine.Vector3.right)
可以發(fā)現(xiàn)確實(shí)移動(dòng)了 我們之前看的都是Unity自己的類,現(xiàn)在我們寫一個(gè)我們自己的類
public class Test
{
public void Speak(string str)
{
Debug.Log(str);
}
}
namespace Theshy
{
public class Test2
{
public void Speak(string str)
{
Debug.Log("Test2: " + str);
}
}
}
然后在lua里
local t=CS.Test()
t:Speak("我々はここで死に")
local t2=CS.Theshy.Test2()
t2:Speak("次の生者に意味を託す")
接下來我們要為實(shí)例化對(duì)象添加腳本,這里別忘了還是使用冒號(hào)
GameObject=CS.UnityEngine.GameObject
local obj5=GameObject("加腳本測(cè)試")
obj5:AddComponent(typeof(CS.MyClass))
Lua調(diào)用C#枚舉
我們先聲明一個(gè)枚舉 然后利用這個(gè)枚舉創(chuàng)建一個(gè)立方體
PrimitiveType=CS.UnityEngine.PrimitiveType
GameObject=CS.UnityEngine.GameObject
local obj=GameObject.CreatePrimitive(PrimitiveType.Cube)--創(chuàng)建立方體
我們這里自己新建一個(gè)枚舉,和剛才我們自己寫類的方法一樣
CS.(命名空間.)枚舉名
Lua使用C# 數(shù)組 List 字典
數(shù)組
我們先在C#腳本中定義一個(gè)數(shù)組,list,字典
public class VectorSet
{
public int[] array = new int[5] { 1, 2, 3, 4, 5 };
public List
public Dictionary
}
然后我們?cè)趌ua中,先寫數(shù)組邏輯
print("lua調(diào)用容器相關(guān)知識(shí)點(diǎn)")
local obj = CS.VectorSet()
--Lua使用C#數(shù)組
--這里不能使用#
print(obj.array.Length)
--訪問元素
print(obj.array[0])
然后是數(shù)組的遍歷,雖然lua中的索引從1開始 但是數(shù)組是C#的數(shù)組,所以還要按C#的來 并且在lua的for循環(huán)默認(rèn)最后條件是<=的,所以要減一
for i=0,obj.array.Length-1 do
print(obj.array[i])
end
如果我想在Lua中創(chuàng)建C#的數(shù)組呢? 由于lua中沒有new,所以不能像C#一樣聲明數(shù)組,但是在Unity中數(shù)組是一個(gè)數(shù)組類,里面提供了一個(gè)靜態(tài)方法,直接創(chuàng)建一個(gè)數(shù)組,所以我們這里直接調(diào)用這個(gè)靜態(tài)方法就可以了
local array2=CS.System.Array.CreateInstance(typeof(CS.System.Int32),10)
print(array2.Length)
print(array2[0])
print(array2[1])
List
對(duì)于List也一樣,遍歷和數(shù)組一模一樣
obj.list:Add(1)
obj.list:Add(2)
obj.list:Add(3)
print(obj.list.Count)
接下來要寫用list創(chuàng)建對(duì)象 分兩個(gè)版本 老版本(2.1.12之前)
local list2=CS.System.Collections.Generic["List`1[System.String]"]()
新版本
local List_String=CS.System.Collections.Generic.List(CS.System.String)--相當(dāng)于得到一個(gè)類
local list3=List_String()
字典
添加元素用add,和上面兩個(gè)一樣 遍歷需要用pairs
for k,v in pairs(obj.doc)do
print(k,v)
end
然后是在Lua中創(chuàng)建一個(gè)字典對(duì)象
local Dic_String_Vector3=CS.System.Collections.Generic.Dictionary(CS.System.String,CS.UnityEngine.Vector3)
local dic2=Dic_String_Vector3()
dic2:Add("123",CS.UnityEngine.Vector3.right)
for i,v in pairs(dic2) do
print(i,v)
end
如果我現(xiàn)在想通過我剛才新建的dic2來訪問鍵值呢? 可以發(fā)現(xiàn)打印的是nil
print(dic2["123"])
這里要用
print(dic2:get_Item("123"))
如果要修改鍵值也要用set_Item
Lua使用C#擴(kuò)展方法
先聲明一個(gè)類和一個(gè)拓展方法
public static class Tools
{
//拓展方法
public static void Move(this testClass obj)
{
Debug.Log(obj.name + "移動(dòng)");
}
}
public class testClass
{
public string name = "Theshy";
public void Speak(string str)
{
Debug.Log(str);
}
public static void Eat()
{
Debug.Log("吃東西");
}
}
然后在lua中調(diào)用
print("擴(kuò)展方法")
testclass=CS.testClass
--使用靜態(tài)方法
testclass.Eat()
--使用成員函數(shù)
local obj=testclass()
obj:Speak("wryyyyyyy")
--使用拓展方法,和使用成員方法方法一致
CS.Tools.Move(obj)
但是會(huì)報(bào)錯(cuò)
所以想要在xLua中使用擴(kuò)展方法,一定要在工具類前面加上特性,然后不要忘了重新生成代碼
如果考慮性能的話,可以把所有l(wèi)ua中需要的東西都寫上LuaCallCSharp,因?yàn)閘ua底層是通過反射來與C#聯(lián)系的,會(huì)有一定性能損失
[XLua.LuaCallCSharp]
//拓展方法
public static void Move(this testClass obj)
{
Debug.Log(obj.name + "移動(dòng)");
}
Lua使用C# ref和out函數(shù)
ref
我們先在Unity中寫個(gè)類
public class Lesson5
{
public int RefFun(int a,ref int b ,ref int c,int d)
{
b = a + d;
c = a - d;
return 100;
}
public int OufFun(int a, out int b, out int c, int d)
{
b = a;
c = d;
return 200;
}
public int RefOutFun(int a, out int b, ref int c)
{
b = a * 10;
c = a * 20;
return 300;
}
}
我們先來看ref的,ref是多返回值,并且需要顯式初始化 首先a是第一個(gè)返回值,接收的就是函數(shù)本身的返回值100,然后b和c分別接ref b ref c
Lesson5=CS.Lesson5
local obj=Lesson5()
local a,b,c=obj.RefFun(1,0,0,1)
print(a)
print(b)
print(c)
out
out不需要傳占位置的值
local obj=Lesson5()
local a,b,c=obj:OutFun(20,30)
print(a)
print(b)
print(c)
函數(shù)重載
我們先寫幾個(gè)簡(jiǎn)單的重載函數(shù)
public class Lesson6
{
public int Calc()
{
return 100;
}
public int Calc(int a,int b)
{
return a+b;
}
public int Calc(int a)
{
return a;
}
public float Calc(float a)
{
return a;
}
}
然后是lua層
local obj=CS.Lesson6()
print(obj:Calc())
print(obj:Calc(15,1))
可以看出lua支持調(diào)用C#中的重載函數(shù)
然后我們?cè)僬{(diào)用后面的
print(obj:Calc(10))
print(obj:Calc(10.2))
lua中只有number類型,而C#中有多個(gè)屬性,所以對(duì)多精度數(shù)據(jù)支持的并不好 用反射來解決多精度重載(效率低,盡量別用)
local m1=typeof(CS.Lesson6):GetMethod("Calc",{typeof(CS.System.Int32)})
local m2=typeof(CS.Lesson6):GetMethod("Calc",{typeof(CS.System.Single)})
local f1=xlua.tofunction(m1)
local f2=xlua.tofunction(m2)
f1(obj,10)
f1(obj,10.2)
lua調(diào)用C# 委托和事件
我們?cè)賹憘€(gè)類,一個(gè)委托,一個(gè)事件,和一個(gè)調(diào)用事件的函數(shù)
public class Lesson7
{
public UnityAction del;
public event UnityAction eventAction;
public void DoEvent()
{
eventAction();
}
}
這里不能直接+=,并且第一次往委托中加函數(shù)是nil不能直接加
local obj=CS.Lesson7()
local fun = function ( )
print("Lua函數(shù)Fun")
end
obj.del=fun
obj.del()--執(zhí)行委托
然后是事件
local obj=CS.Lesson7()
local fun2=function()
print("事件加的函數(shù)")
end
obj:eventAction("+",fun2)
obj:eventAction("+",fun2)
obj:DoEvent()
相應(yīng)的,想減少調(diào)用就用減號(hào) 如果我想清除事件,不能直接像委托del=nil 而是需要在C#層加一個(gè)函數(shù)
public void ClearEvent()
{
eventAction = null;
}
然后
obj:ClearEvent()
Lua使用C#二維數(shù)組
我們先在C#中聲明一個(gè)二維數(shù)組
public class Lesson8
{
public int[,] array = new int[2, 3] { { 1, 2, 3 }, { 4, 5, 6 } };
}
我們?cè)趌ua中先取到一個(gè)簡(jiǎn)單的二維數(shù)組
print("二維數(shù)組")
local obj=CS.Lesson8()
--獲取長(zhǎng)度
print(obj.array:GetLength(0))
print(obj.array:GetLength(1))
在Lua中訪問C#的二維數(shù)組不能通過[0,0]或[0][0]來訪問 和之前array一樣,二維數(shù)組提供了一個(gè)方法來返回值
print(obj.array:GetValue(0,0))
print(obj.array:GetValue(0,1))
然后是二維數(shù)組的遍歷
for i=0,obj:GetLength(0)-1 do
for j=0,obj:GetLength(1)-1 do
print(obj.GetValue(i,j))
end
end
Lua中null與nil的比較
我們現(xiàn)在實(shí)現(xiàn)一個(gè)需求,先實(shí)例化一個(gè)物體嗎,然后判斷其有沒有剛體組件,如果沒有再加
GameObject=CS.UnityEngine.GameObject
Rigidbody=CS.UnityEngine.Rigidbody
local obj=GameObject("測(cè)試加腳本")
local rig=obj:GetComponent(typeof(Rigidbody))
print(rig)
if rig==nil then
rig=obj:AddComponent(typeof(Rigidbody))
end
print(rig)
這里發(fā)現(xiàn)即使沒有腳本,也不會(huì)新加,可以通過測(cè)試發(fā)現(xiàn)if rig==nil 這行邏輯沒進(jìn) 原因是因?yàn)閚ull 和nil不是一個(gè)東西 所以這里要用 我們可以自己在lua中的Main文件中寫一個(gè)邏輯
function IsNull( obj )
if obj==nil or obj:Equals(nil) then
return true
else return false
end
我們也可以使用一個(gè)擴(kuò)展方法,這樣就把核心邏輯寫在C#中而不是lua中了
系統(tǒng)類型與Lua互相訪問
如果我需要在lua中使用一個(gè)只讀的代碼(系統(tǒng)庫(kù)或三方庫(kù)),不能加LuaCallCSharp代碼,應(yīng)該怎么做: 假如說我現(xiàn)在需要為UnityAction< float >加特性 我們寫這么一段代碼
public static class Lesson10
{
[CSharpCallLua]
public static List
{
typeof(UnityAction
};
}
然后點(diǎn)生成代碼
lua使用C#協(xié)程
這里我們?yōu)樾聞?chuàng)建的Gameobject添加了一個(gè)腳本,然后通過這個(gè)腳本調(diào)用StartCoroutine
GameObject =CS.UnityEngine.GameObject
WaitForSeconds=CS.UnityEngine.WaitForSeconds
local obj=GameObject("Coroutine")
local mono=obj:AddComponent(typeof(CS.LuaCallCSharp))
--希望用來被開啟的協(xié)程
fun=function ()
local a=1
while true do
coroutine.yield()
print(a)
a=a+1
end
end
mono:StartCoroutine(fun)
但是這樣會(huì)報(bào)錯(cuò) 所以不能通過StartCoroutine來直接調(diào)用fun
所以我們使用一個(gè)xlua官方自帶的一個(gè)工具表
util=require("xlua.util")
mono:StartCoroutine(util.cs_generator(fun))
Lua使用C#泛型
我們先在C#里寫幾個(gè)泛型函數(shù)
public class Lesson12
{
public interface ITest
{
}
public class TestFather
{
}
public class TestChild:TestFather,ITest
{
}
public void TestFun1
{
Debug.Log("有參數(shù)有約束的泛型方法");
}
public void TestFun2
{
Debug.Log("有參數(shù),無約束");
}
public void TestFun3
{
Debug.Log("無參數(shù),有約束");
}
public void TestFun4
{
Debug.Log("有參數(shù),有約束,約束不是類是接口");
}
}
然后來到lua 我們先來測(cè)試第一個(gè)
local obj=CS.Lesson12()
local child = CS.Lesson12.TestChild()
local father = CS.Lesson12.TestFather()
obj:TestFun1(child,father)
obj:TestFun1(father,child)
然后我們來調(diào)用第二組
報(bào)錯(cuò)了,可以發(fā)現(xiàn)lua中不支持不約束的泛型 然后第三組,可以發(fā)現(xiàn)lua也不支持無參帶約束的泛型 然后看最后一組 因?yàn)樵贑#中child類繼承自接口,所以用child傳進(jìn)去
obj:TestFun4(child)
可以發(fā)現(xiàn)4也不行,lua中不支持非class的約束 接下來講讓上面不支持的泛型函數(shù)變得能用
local testFun2=xlua.get_generic_method(CS.Lesson12,"TestFun2")
local testFun2_R=testFun2(CS.System.Int32)
--第一個(gè)參數(shù)傳調(diào)用函數(shù)的對(duì)象
testFun2_R(obj,1)
柚子快報(bào)激活碼778899分享:游戲 設(shè)計(jì)模式 xLua詳解
推薦文章
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。