Lua 是一個小巧的腳本語言。其設計目的是爲了通過靈活嵌入應用程序中從而爲應用程序提供靈活的擴展和定製功能。Lua由標準C編寫而成,幾乎在所有操作系統和平臺上都可以編譯,運行。Lua並沒有提供強大的庫,這是由它的定位決定的。所以Lua不適合作爲開發獨立應用程序的語言。
Lua腳本可以很容易的被C/C++ 代碼調用,也可以反過來調用C/C++的函數,這使得Lua在應用程序中可以被廣泛應用。
Lua是Unity3D熱更新技術實現的一種方式,分別有uLua、toLua、xLua等,國內熱更新用xLua實現的較多,xLua是騰訊發佈的。
今天主要介紹xLua和Unity3D實現方式。
Lua教程:https://www.w3cschool.cn/lua/lua-tutorial.html
xLua的源碼:https://github.com/Tencent/xLua
首先:下載xlua的Plugins,xLua源碼中就有,導入Unity的Assets下的Plugins文件夾
***其次:做xLua的配置(可能下載的Plugins自帶配置腳本,已經配置好了), xLua所有的配置都支持三種方式:打標籤;靜態列表;動態列表。
配置有兩必須兩建議:
- 列表方式均必須是static的字段/屬性
- 列表方式均必須放到一個static類
- 建議不用標籤方式
- 建議列表方式配置放Editor目錄(如果是Hotfix配置,而且類位於Assembly-CSharp.dll之外的其它dll,必須放Editor目錄)
其實,配置這塊我也弄的不是很清楚,具體可看源碼中的配置文檔,大家見諒,有什麼見解可交流。
//XLua.BlackList配置方式
[BlackList]
public static List<List<string>> BlackList = new List<List<string>>() {
new List<string>(){"UnityEngine.GameObject", "networkView"},
new List<string>(){"System.IO.FileInfo", "GetAccessControl","System.Security.AccessControl.AccessControlSections"},
..........
};
C# 訪問Lua
//***Lua的代碼***
string script = @"
a = 1
b = 'hello world'
c = true
d = {
f1 = 12, f2 = 34,
1, 2, 3,
add = function(self, a, b)
print('d.add called')
return a + b
end
}
function e()
print('i am e')
end
function f(a, b)
print('a', a, 'b', b)
return 1, {f1 = 1024}
end
function ret_e()
print('ret_e called')
return e
end";
*****Unity3D的C#代碼:*****(主要部分)
// 固定標準寫法部分
LuaEnv luaenv = null;
void Start(){
luaenv = new LuaEnv();
//script就是加載過來的Lua語言腳本
luaenv.DoString(script);
}
void Update()
{
if (luaenv != null){
luaenv.Tick();
}
}
void OnDestroy()
{
luaenv.Dispose();
}
//訪問數據部分
void GetInfo(){
//a、b、c表示對面的變量名稱,數字類型統稱是Number類型,沒有int、double等之分。
int a = luaenv.Global.Get<int>("a");
string b = luaenv.Global.Get<string>("b")
bool c = luaenv.Global.Get<bool>("c")
///映射到有對應字段的class
public class DClass
{
public int f1;
public int f2;
}
DClass d = luaenv.Global.Get<DClass>("d");
//或者
//映射到Dictionary<string, double>,by value
Dictionary<string, double> d1 = luaenv.Global.Get<Dictionary<string, double>>("d");
//或者
////映射到interface實例
[CSharpCallLua]
public interface ItfD
{
int f1 { get; set; }
int f2 { get; set; }
int add(int a, int b);
}
ItfD d3 = luaenv.Global.Get<ItfD>("d");
//映射到一個delgate,要求delegate加到生成列表,否則返回null
Action e = luaenv.Global.Get<Action>("e");
[CSharpCallLua]
public delegate int FDelegate(int a, string b, out DClass c);
FDelegate f = luaenv.Global.Get<FDelegate>("f");
//delegate可以返回更復雜的類型,甚至是另外一個delegate
[CSharpCallLua]
public delegate Action GetE();
GetE ret_e = luaenv.Global.Get<GetE>("ret_e");//返回ret_e也是一個函數,可以當成一個函數委託
.......
}
Lua訪問C#(資源熱更新最常用的調用)
//Unity3D的C#代碼
固定代碼跟上述C# 訪問xLua中C#固定標準代碼一樣,這裏就不寫了
//C#實現的需要Lua語言訪問的類和屬性方法
注意:這裏要訪問的類,接口,結構體,枚舉緊接上行都要加[LuaCallCSharp]
namespace Tutorial
{
[LuaCallCSharp]
public class BaseClass
{
public static void BSFunc()
{
Debug.Log("Derived Static Func, BSF = " + BSF);
}
public static int BSF = 1;
public void BMFunc()
{
Debug.Log("Derived Member Func, BMF = " + BMF);
}
public int BMF { get; set; }
}
public struct Param1
{
public int x;
public string y;
}
[LuaCallCSharp]
public enum TestEnum
{
E1,
E2
}
[LuaCallCSharp]
public class DerivedClass : BaseClass
{
[LuaCallCSharp]
public enum TestEnumInner
{
E3,
E4
}
public void DMFunc()
{
Debug.Log("Derived Member Func, DMF = " + DMF);
}
public int DMF { get; set; }
public double ComplexFunc(Param1 p1, ref int p2, out string p3, Action luafunc, out Action csfunc)
{
Debug.Log("P1 = {x=" + p1.x + ",y=" + p1.y + "},p2 = " + p2);
luafunc();
p2 = p2 * p1.x;
p3 = "hello " + p1.y;
csfunc = () =>
{
Debug.Log("csharp callback invoked!");
};
return 1.23;
}
public void TestFunc(int i)
{
Debug.Log("TestFunc(int i)");
}
public void TestFunc(string i)
{
Debug.Log("TestFunc(string i)");
}
public static DerivedClass operator +(DerivedClass a, DerivedClass b)
{
DerivedClass ret = new DerivedClass();
ret.DMF = a.DMF + b.DMF;
return ret;
}
public void DefaultValueFunc(int a = 100, string b = "cccc", string c = null)
{
UnityEngine.Debug.Log("DefaultValueFunc: a=" + a + ",b=" + b + ",c=" + c);
}
public void VariableParamsFunc(int a, params string[] strs)
{
UnityEngine.Debug.Log("VariableParamsFunc: a =" + a);
foreach (var str in strs)
{
UnityEngine.Debug.Log("str:" + str);
}
}
public TestEnum EnumTestFunc(TestEnum e)
{
Debug.Log("EnumTestFunc: e=" + e);
return TestEnum.E2;
}
public Action<string> TestDelegate = (param) =>
{
Debug.Log("TestDelegate in c#:" + param);
};
public event Action TestEvent;
public void CallEvent()
{
TestEvent();
}
public ulong TestLong(long n)
{
return (ulong)(n + 1);
}
class InnerCalc : ICalc
{
public int add(int a, int b)
{
return a + b;
}
public int id = 100;
}
public ICalc GetCalc()
{
return new InnerCalc();
}
public void GenericMethod<T>()
{
Debug.Log("GenericMethod<" + typeof(T) + ">");
}
}
[LuaCallCSharp]
public interface ICalc
{
int add(int a, int b);
}
[LuaCallCSharp]
public static class DerivedClassExtensions
{
public static int GetSomeData(this DerivedClass obj)
{
Debug.Log("GetSomeData ret = " + obj.DMF);
return obj.DMF;
}
public static int GetSomeBaseData(this BaseClass obj)
{
Debug.Log("GetSomeBaseData ret = " + obj.BMF);
return obj.BMF;
}
public static void GenericMethodOfString(this DerivedClass obj)
{
obj.GenericMethod<string>();
}
}
[LuaCallCSharp]
public struct A
{
public int a;
}
[LuaCallCSharp]
public struct B
{
public A b;
public double c;
}
void Foo(B b)
{
Debug.Log(" ");
}
}
//xLua訪問Unity3D對象以及代碼
//--表示Lua語言註釋
function demo()
--new C#對象,helloworld表示創建的Unity3D對象名字,這塊也可以省略
local newGameObj = CS.UnityEngine.GameObject('helloworld')
--訪問靜態屬性,方法
local GameObject = CS.UnityEngine.GameObject
print('UnityEngine.Time.deltaTime:', CS.UnityEngine.Time.deltaTime) --讀靜態屬性
CS.UnityEngine.Time.timeScale = 0.5 --寫靜態屬性
GameObject.Find('helloworld') --靜態方法調用,表示查找Unity3D的helloworld對象
CS.UnityEngine.GameObject.Find("對象"):GetCompontent("腳本名")
--typeof,Unity3D添加腳本
newGameObj:AddComponent(typeof(CS.UnityEngine.ParticleSystem))
--訪問類成員屬性,方法,Tutorial表示命名空間,DerivedClass表示類名
local DerivedClass = CS.Tutorial.DerivedClass
local testobj = DerivedClass()
testobj.DMF = 1024--設置成員屬性調用
testobj:DMFunc()--成員方法調用
DerivedClass.BSF = 2048--靜態屬性調用
DerivedClass.BSFunc();--靜態方法調用
--複雜方法調用
local ret, p2, p3, csfunc = testobj:ComplexFunc({x=3, y = 'john'}, 100, function()
print('i am lua callback')
end)
print('ComplexFunc ret:', ret, p2, p3, csfunc)
csfunc()
--重載方法調用
testobj:TestFunc(100)
testobj:TestFunc('hello')
--操作符
local testobj2 = DerivedClass()
testobj2.DMF = 2048
print('(testobj + testobj2).DMF = ', (testobj + testobj2).DMF)
--默認值
testobj:DefaultValueFunc(1)
testobj:DefaultValueFunc(3, 'hello', 'john')
--可變參數
testobj:VariableParamsFunc(5, 'hello', 'john')
--Extension methods
print(testobj:GetSomeData())
print(testobj:GetSomeBaseData()) --訪問基類的Extension methods
testobj:GenericMethodOfString() --通過Extension methods實現訪問泛化方法
--枚舉類型
local e = testobj:EnumTestFunc(CS.Tutorial.TestEnum.E1)
print(CS.Tutorial.TestEnum.__CastFrom(1), CS.Tutorial.TestEnum.__CastFrom('E1'))
--Delegate
testobj.TestDelegate('hello') --直接調用
local function lua_delegate(str)
print('TestDelegate in lua:', str)
end
testobj.TestDelegate = lua_delegate + testobj.TestDelegate --combine,這裏演示的是C#delegate作爲右值,左值也支持
testobj.TestDelegate('hello')
testobj.TestDelegate = testobj.TestDelegate - lua_delegate --remove
testobj.TestDelegate('hello')
--事件
local function lua_event_callback1() print('lua_event_callback1') end
local function lua_event_callback2() print('lua_event_callback2') end
testobj:TestEvent('+', lua_event_callback1)
testobj:CallEvent()
testobj:TestEvent('+', lua_event_callback2)
testobj:CallEvent()
testobj:TestEvent('-', lua_event_callback1)
testobj:CallEvent()
testobj:TestEvent('-', lua_event_callback2)
--64位支持
local l = testobj:TestLong(11)
print(type(l), l, l + 100, 10000 + l)
--複雜類型使用(使用table自動轉換)
testobj:Foo({b={a=100},c=200})
--cast
local calc = testobj:GetCalc()
print('assess instance of InnerCalc via reflection', calc:add(1, 2))
assert(calc.id == 100)
//強轉類型(一般遇到接口會使用的到)
cast(calc, typeof(CS.Tutorial.ICalc))
print('cast to interface ICalc', calc:add(1, 2))
assert(calc.id == nil)
end
//可以直接執行,也可以協程執行
--demo()
--協程下使用
local co = coroutine.create(function()
demo()
end)
assert(coroutine.resume(co))
簡單的xLua和Unity3D實現熱更新技術代碼介紹,但是萬變不離其宗,就是這些簡單的語句實現。
最後重點:將Lua腳本寫好後,可放在StreamingAssets文件夾下面,使用異步加載讀取