LuaEnv luaEnv = new LuaEnv();
先构造lua的运行环境
c#中执行lua
执行一条语句
//执行一句lua语句
luaEnv.DoString("print('i love you 3000');");
执行一个脚本
//执行一个lua脚本
luaText = Resources.Load<TextAsset>("ForMyTest.lua");
luaEnv.DoString(luaText.text);
luaEnv.DoString("require 'ForMyTest'");
c#访问lua
lua脚本:
print("lua is succeed")
a=1
b=1.2
c="sdfgsddfgfsdgf"
d=function ( ... )
print("this is a function")
end
person={
name = "leander",
age = 24,
--self参数,代表当前table对象
sum = function(self,a,b)
print("a+b="+(a+b))
end
}
访问基本数据类型
int a = luaEnv.Global.Get<int>("a");
float b = luaEnv.Global.Get<float>("b");
string c = luaEnv.Global.Get<string>("d");
访问table类型
映射到普通类或者struct
这种方式下xLua会帮你new一个实例,并把对应的字段赋值过去。table的属性可以多于或者少于class的属性。可以嵌套其它复杂类型。要注意的是,这个过程是值拷贝,如果class比较复杂代价会比较大。而且修改class的字段值不会同步到table,反过来也不会。这个功能可以通过把类型加到GCOptimize生成降低开销。实现方式如下:
Person p = luaEnv.Global.Get<Person>("person");
print(p.name);
print(p.age);
class Person
{
public int age;
public string name;
public Action<int, int> sum;
}
PS:这种方式不能映射lua里面的函数
映射到接口
这种方式依赖于生成代码(如果没生成代码会抛InvalidCastException异常),代码生成器会生成这个interface的实例,如果get一个属性,生成代码会get对应的table字段,如果set属性也会设置对应的字段。甚至可以通过interface的方法访问lua的函数。
这个过程是引用拷贝,修改接口会影响到lua里面的变量
//3.2通过接口来映射 因为版本问题所以不成功
IPerson ip = luaEnv.Global.Get<IPerson>("person");
ip.name = "wzy";
print(ip.name);
[CSharpCallLua] //如果不加这个属性就会抛InvalidCastException异常
interface IPerson
{
//接口不能包含字段,将其转换成属性
string name { get; set; }
int age { get; set; }
//函数名称需要与lua文件函数名一致
void sum(int a, int b);
}
映射到字典或者list
List只能映射值,无法映射key-value形式;Dictionary只能映射key-value形式,无法映射值形式。
List<object> lp = luaEnv.Global.Get<List<object>>("person");
print(lp.Count);
Dictionary<string, object> dp = luaEnv.Global.Get<Dictionary<string, object>>("person");
print(dp.Count);
映射到luatable类
这种方式好处是不需要生成代码,但也有一些问题,比如慢,比方式2要慢一个数量级,比如没有类型检查。若table中包含其他非key-value形式,代码将会报InvalidCastException异常
获取全局函数
映射到delegate
映射到luafunction
可以传入多个参数,返回值是个Object数组
LuaFunction f = luaEnv.Global.Get<LuaFunction>("d");
object[] results = f.Call(1, 2);
print(results[0]);
demon:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using XLua;
using System;
namespace Tutorial
{
public class CSCallLua : MonoBehaviour
{
LuaEnv luaenv = null;
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
";
public class DClass
{
public int f1;
public int f2;
}
[CSharpCallLua]
public interface ItfD
{
int f1 { get; set; }
int f2 { get; set; }
int add(int a, int b);
}
[CSharpCallLua]
public delegate int FDelegate(int a, string b, out DClass c);
[CSharpCallLua]
public delegate Action GetE();
// Use this for initialization
void Start()
{
luaenv = new LuaEnv();
luaenv.DoString(script);
Debug.Log("_G.a = " + luaenv.Global.Get<int>("a"));
Debug.Log("_G.b = " + luaenv.Global.Get<string>("b"));
Debug.Log("_G.c = " + luaenv.Global.Get<bool>("c"));
DClass d = luaenv.Global.Get<DClass>("d");//映射到有对应字段的class,by value
Debug.Log("_G.d = {f1=" + d.f1 + ", f2=" + d.f2 + "}");
Dictionary<string, double> d1 = luaenv.Global.Get<Dictionary<string, double>>("d");//映射到Dictionary<string, double>,by value
Debug.Log("_G.d = {f1=" + d1["f1"] + ", f2=" + d1["f2"] + "}, d.Count=" + d1.Count);
List<double> d2 = luaenv.Global.Get<List<double>>("d"); //映射到List<double>,by value
Debug.Log("_G.d.len = " + d2.Count);
ItfD d3 = luaenv.Global.Get<ItfD>("d"); //映射到interface实例,by ref,这个要求interface加到生成列表,否则会返回null,建议用法
d3.f2 = 1000;
Debug.Log("_G.d = {f1=" + d3.f1 + ", f2=" + d3.f2 + "}");
Debug.Log("_G.d:add(1, 2)=" + d3.add(1, 2));
LuaTable d4 = luaenv.Global.Get<LuaTable>("d");//映射到LuaTable,by ref
Debug.Log("_G.d = {f1=" + d4.Get<int>("f1") + ", f2=" + d4.Get<int>("f2") + "}");
Action e = luaenv.Global.Get<Action>("e");//映射到一个delgate,要求delegate加到生成列表,否则返回null,建议用法
e();
FDelegate f = luaenv.Global.Get<FDelegate>("f");
DClass d_ret;
int f_ret = f(100, "John", out d_ret);//lua的多返回值映射:从左往右映射到c#的输出参数,输出参数包括返回值,out参数,ref参数
Debug.Log("ret.d = {f1=" + d_ret.f1 + ", f2=" + d_ret.f2 + "}, ret=" + f_ret);
GetE ret_e = luaenv.Global.Get<GetE>("ret_e");//delegate可以返回更复杂的类型,甚至是另外一个delegate
e = ret_e();
e();
LuaFunction d_e = luaenv.Global.Get<LuaFunction>("e");
d_e.Call();
}
// Update is called once per frame
void Update()
{
if (luaenv != null)
{
luaenv.Tick();
}
}
void OnDestroy()
{
luaenv.Dispose();
}
}
}
Lua调用C#
new C#对象
你在C#这样new一个对象:
var newGameObj = new UnityEngine.GameObject();
对应到Lua是这样:
local newGameObj = CS.UnityEngine.GameObject()
基本类似,除了:
1. lua里头没有new关键字;
2. 所有C#相关的都放到CS下,包括构造函数,静态成员属性、方法;
如果有多个构造函数呢?放心,xlua支持重载,比如你要调用GameObject的带一个string参数的构造函数,这么写:
local newGameObj2 = CS.UnityEngine.GameObject('helloworld')
访问C#静态属性,方法
读静态属性
CS.UnityEngine.Time.deltaTime
写静态属性
CS.UnityEngine.Time.timeScale = 0.5
调用静态方法
CS.UnityEngine.GameObject.Find('helloworld')
小技巧:如果需要经常访问的类,可以先用局部变量引用后访问,除了减少敲代码的时间,还能提高性能:
local GameObject = CS.UnityEngine.GameObject
GameObject.Find('helloworld')
访问C#成员属性,方法
读成员属性
testobj.DMF
写成员属性
testobj.DMF = 1024
调用成员方法
注意:调用成员方法,第一个参数需要传该对象,建议用冒号语法糖,如下
testobj:DMFunc()
父类属性,方法
xlua支持(通过派生类)访问基类的静态属性,静态方法,(通过派生类实例)访问基类的成员属性,成员方法
参数的输入输出属性(out,ref)
Lua调用侧的参数处理规则:C#的普通参数算一个输入形参,ref修饰的算一个输入形参,out不算,然后从左往右对应lua 调用侧的实参列表;
Lua调用侧的返回值处理规则:C#函数的返回值(如果有的话)算一个返回值,out算一个返回值,ref算一个返回值,然后从左往右对应lua的多返回值。
重载方法
直接通过不同的参数类型进行重载函数的访问,例如:
testobj:TestFunc(100)
testobj:TestFunc('hello')
将分别访问整数参数的TestFunc和字符串参数的TestFunc。
注意:xlua只一定程度上支持重载函数的调用,因为lua的类型远远不如C#丰富,存在一对多的情况,比如C#的int,float,double都对应于lua的number,上面的例子中TestFunc如果有这些重载参数,第一行将无法区分开来,只能调用到其中一个(生成代码中排前面的那个)
操作符
支持的操作符有:+,-,*,/,==,一元-,<,<=, %,[]
参数带默认值的方法
和C#调用有默认值参数的函数一样,如果所给的实参少于形参,则会用默认值补上。
可变参数方法
对于C#的如下方法:
void VariableParamsFunc(int a, params string[] strs)
可以在lua里头这样调用:
testobj:VariableParamsFunc(5, 'hello', 'john')
使用Extension methods
在C#里定义了,lua里就能直接使用。
泛化(模版)方法
不直接支持,可以通过Extension methods功能进行封装后调用。
枚举类型
枚举值就像枚举类型下的静态属性一样。
testobj:EnumTestFunc(CS.Tutorial.TestEnum.E1)
上面的EnumTestFunc函数参数是Tutorial.TestEnum类型的。
枚举类支持__CastFrom方法,可以实现从一个整数或者字符串到枚举值的转换,例如:
CS.Tutorial.TestEnum.__CastFrom(1)
CS.Tutorial.TestEnum.__CastFrom('E1')
delegate使用(调用,+,-)
C#的delegate调用:和调用普通lua函数一样
+操作符:对应C#的+操作符,把两个调用串成一个调用链,右操作数可以是同类型的C# delegate或者是lua函数。
-操作符:和+相反,把一个delegate从调用链中移除。
Ps:delegate属性可以用一个luafunction来赋值。
event
比如testobj里头有个事件定义是这样:public event Action TestEvent;
增加事件回调
testobj:TestEvent('+', lua_event_callback)
移除事件回调
testobj:TestEvent('-', lua_event_callback)
64位整数支持
Lua53版本64位整数(long,ulong)映射到原生的64位整数,而luajit版本,相当于lua5.1的标准,本身不支持64位,xlua做了个64位支持的扩展库,C#的long和ulong都将映射到userdata:
支持在lua里头进行64位的运算,比较,打印
支持和lua number的运算,比较
要注意的是,在64扩展库中,实际上只有int64,ulong也会先强转成long再传递到lua,而对ulong的一些运算,比较,我们采取和java一样的支持方式,提供一组API,详情请看API文档。
C#复杂类型和table的自动转换
对于一个有无参构造函数的C#复杂类型,在lua侧可以直接用一个table来代替,该table对应复杂类型的public字段有相应字段即可,支持函数参数传递,属性赋值等,例如:
C#下B结构体(class也支持)定义如下:
public struct A
{
public int a;
}
public struct B
{
public A b;
public double c;
}
某个类有成员函数如下:
void Foo(B b)
在lua可以这么调用
obj:Foo({b = {a = 100}, c = 200})
获取类型(相当于C#的typeof)
比如要获取UnityEngine.ParticleSystem类的Type信息,可以这样
typeof(CS.UnityEngine.ParticleSystem)
“强”转
lua没类型,所以不会有强类型语言的“强转”,但有个有点像的东西:告诉xlua要用指定的生成代码去调用一个对象,这在什么情况下能用到呢?有的时候第三方库对外暴露的是一个interface或者抽象类,实现类是隐藏的,这样我们无法对实现类进行代码生成。该实现类将会被xlua识别为未生成代码而用反射来访问,如果这个调用是很频繁的话还是很影响性能的,这时我们就可以把这个interface或者抽象类加到生成代码,然后指定用该生成代码来访问:
cast(calc, typeof(CS.Tutorial.Calc))
上面就是指定用CS.Tutorial.Calc的生成代码来访问calc对象。
demon:
using UnityEngine;
using System.Collections;
using System;
using XLua;
using System.Collections.Generic;
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>();
}
}
}
public class LuaCallCs : MonoBehaviour
{
LuaEnv luaenv = null;
string script = @"
function demo()
--new C#对象
local newGameObj = CS.UnityEngine.GameObject()
local newGameObj2 = CS.UnityEngine.GameObject('helloworld')
print(newGameObj, newGameObj2)
--访问静态属性,方法
local GameObject = CS.UnityEngine.GameObject
print('UnityEngine.Time.deltaTime:', CS.UnityEngine.Time.deltaTime) --读静态属性
CS.UnityEngine.Time.timeScale = 0.5 --写静态属性
print('helloworld', GameObject.Find('helloworld')) --静态方法调用
--访问成员属性,方法
local DerivedClass = CS.Tutorial.DerivedClass
local testobj = DerivedClass()
testobj.DMF = 1024--设置成员属性
print(testobj.DMF)--读取成员属性
testobj:DMFunc()--成员方法
--基类属性,方法
print(DerivedClass.BSF)--读基类静态属性
DerivedClass.BSF = 2048--写基类静态属性
DerivedClass.BSFunc();--基类静态方法
print(testobj.BMF)--读基类成员属性
testobj.BMF = 4096--写基类成员属性
testobj:BMFunc()--基类方法调用
--复杂方法调用
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(e, e == CS.Tutorial.TestEnum.E2)
print(CS.Tutorial.TestEnum.__CastFrom(1), CS.Tutorial.TestEnum.__CastFrom('E1'))
print(CS.Tutorial.DerivedClass.TestEnumInner.E3)
assert(CS.Tutorial.BaseClass.TestEnumInner == nil)
--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)
--typeof
newGameObj:AddComponent(typeof(CS.UnityEngine.ParticleSystem))
--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()
print('------------------------------------------------------')
demo()
end)
assert(coroutine.resume(co))
";
// Use this for initialization
void Start()
{
luaenv = new LuaEnv();
luaenv.DoString(script);
}
// Update is called once per frame
void Update()
{
if (luaenv != null)
{
luaenv.Tick();
}
}
void OnDestroy()
{
luaenv.Dispose();
}
}