感觉学习的笔记还是放在网上查询的时候比较方便,文章初看会感觉很难理清,需要耐心去看,对不起自己对不起翻到我文章的同学,不废话直接上代码。
1.C#调用Lua (耐心!耐心!耐心!)
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
//这个示例涉及到的Lua脚本,将文本复制过来
/*
a = 100.1
str = "hahaha"
isDie = false
person = {
name = "xumeixi";
age1 = 26,108,18,"haha",true,2.34;
add = function(self,a,b)
print(a+b)
end
--[[
--这是注释掉的两种方法实现方式
function person:add(a,b)--默认带一个self的参数,代表当前table
print(a+b)
end
function person.add(self,a,b)
print(a+b)
end
--这是注释掉的两种方法实现方式
--]]
}
function add()
print("调用全局Function:add")
end
function cut(a,b)
print("相减获得:"..(a-b))
return a - b
end
function add_cut_mul_div(a,b)
return a+b,a-b,a*b,a/b
end
*/
///
/// C#调用Lua代码示例
///
public class HelloXLua_CSharpCallLua : MonoBehaviour {
//创建Lua虚拟机
LuaEnv luaenv = new LuaEnv();
// Use this for initialization
void Start () {
//普通加载lua脚本的形式
luaenv.DoString("require 'Lua/CSharpCallLua'");
//直接加载文本形式的lua代码
//luaenv.DoString(script);
//1.C#访问Lua中的全局基本数据类型
//GetGlobalData();
//2.1.1.读取lua脚本中的数据映射到普通class或struct(比较耗费性能)
//GetTable();
//2.1.2.读取文本形式的lua代码映射到普通class或struct(比较耗费性能)
//GetTableScript();
//2.2.映射到一个interface(接口)
//GetOrSetData();
//2.3.更轻量级(代码量较少)的by value方式:映射到Dictionary<>、List<>
GetDataByDictionatyOrList();
//2.4.另外一种by ref方式:映射到LuaTable类(不推荐,一般用2.2映射到一个interface的方法)
//GetDataByLUaTable();
//3.1使用Get方法,不同的是类型映射
//GetDataByFunction();
//3.2.映射到LuaFunction(性能较慢)
//GetDataByLuaFunction();
}
#region 1.C#访问Lua中的全局基本数据类型
void GetGlobalData()
{
double a = luaenv.Global.Get("a");
print("a = " + a);//a = 100.1
string str = luaenv.Global.Get("str");
print("str = " + str);//str = hahaha
bool isDie = luaenv.Global.Get("isDie");
print("isDie = " + isDie);//isDie = False
a = 10;
a = luaenv.Global.Get("a");
print(a);//a = 100.1 说明在这种直接获取的情况下不能改变Lua脚本中的变量值,只能获取
}
#endregion
#region 2.C#访问Lua中全局的table类型
//定义一个class,有对应于table的字段的public属性,而且有无参数构造函数即可,比如对于{f1 = 100, f2 =100}可以定义一个包含public int f1;public int f2;的class。
//这种方式下xLua会帮你new一个实例,并把对应的字段赋值过去。
//table的属性可以多于或者少于class的属性(没有对应的属性就不会映射)。可以嵌套其它复杂类型。
//要注意的是,这个过程是值拷贝,如果class比较复杂代价会比较大。而且修改class的字段值不会同步到table,反过来也不会。
//这个功能可以通过把类型加到GCOptimize生成降低开销
class Person
{
public string name;
public int age1;
public int age2;
}
class DTable
{
public int f1;
public int f2;
}
#region 2.1.1.读取lua脚本中的数据映射到普通class或struct(比较耗费性能)
void GetTable()
{
Person p = luaenv.Global.Get("person");
print(p.name + "-" + p.age1 + "-" + p.age2);
p.name = "xudada";
luaenv.DoString("print(person.name)");
}
#endregion
#region 2.1.2.读取文本形式的lua代码映射到普通class或struct(比较耗费性能)
string script =
@"
person = {
name = 'xumeixi';
age1 = 26,108,18,'haha',true;
}
d = {
f1 = 10;
f2 = 20;
add = function(self,a,b)
print('d.add called')
return a + b;
end
}";
void GetTableScript()
{
Person p = luaenv.Global.Get("person");
print(p.name + "-" + p.age1 + "-" + p.age2);
p.name = "xudada";
luaenv.DoString("print(person.name)");
DTable d = luaenv.Global.Get("d");
print("f1+f2=" + d.f1 + d.f2);
}
#endregion
#region 2.2.映射到一个interface(接口)
//这种方式依赖于生成代码(如果没生成代码会抛InvalidCastException异常),代码生成器会生成这个interface的实例,如果get一个属性,
//生成代码会get对应的table字段,如果set属性也会设置对应的字段。甚至可以通过interface的方法访问lua的函数。
//使用[CSharpCallLua]标记后就会生成代码,不适用标记就会报错InvalidCastException异常
[CSharpCallLua]
interface IPerson
{
string name { get; set; }
int age1 { get; set; }
void add(int a, int b);
}
//可以获取和设置数据,双向的,改变C#这边的值Lua那边的也会改变
void GetOrSetData()
{
IPerson p = luaenv.Global.Get("person");
print(p.name + "-" + p.age1);
p.name = "xudada";
luaenv.DoString("print(person.name)");
p.add(28, 30);
}
#endregion
#region 2.3.更轻量级(代码量较少)的by value方式:映射到Dictionary<>、List<>
//不想定义class或者interface的话,可以考虑用这个,前提table下key和value的类型都是一致的。
//映射成Dictionary指只会射有key值的,而List只能映射无Key值的,两者互补可以得到全部数据(这里的有无Key值只lua代码中的一些值没有指定Key,age1 = 26,108,18,"haha",true,2.34;这里只有26是有key值的)
void GetDataByDictionatyOrList()
{
//因为value类型不确定,因此用object代替,只会映射有key的(有变量名的,包括函数)
Dictionary dict = luaenv.Global.Get>("person");
foreach(string key in dict.Keys)
{
print("dic:" + key + "-" + dict[key]);
}
//只会映射没有key(没有变量名)的数据,包括所有类型(int,string,bool等)
List
2.Lua调用C# (耐心!耐心!耐心!)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
using System;
//添加了[LuaCallCSharp]标签的类才能被Lua调用
[LuaCallCSharp]
public class BaseClass
{
public static int BSF = 1;
public static void BSFunc()
{
Debug.Log("Driven Static Func,BSF = " + BSF);
}
public int BMF { get; set; }
public void BMFunc()
{
Debug.Log("Driven Member Func, BMF = " + BMF);
}
}
public struct Paraml
{
public int x;
public string y;
}
public enum TestEnum
{
E1,
E2
}
[LuaCallCSharp]
public class DrivenClass:BaseClass
{
public int DMF { get; set; }
public void DMFunc()
{
Debug.Log("Driven Member Func, DMF = " + DMF);
}
//复杂函数:C#和Lua相互调用
public double ComplexFunc(Paraml 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 DrivenClass operator +(DrivenClass a, DrivenClass b)
{
DrivenClass ret = new DrivenClass();
ret.DMF = a.DMF + b.DMF;
return ret;
}
//函数包含默认参数
public void DefaultValueFunc(int a = 100, string b = "bbbbbb", string c = null)
{
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);
}
}
//通过Extension methods实现访问泛化方法
public void GenericMethod()
{
Debug.Log("GenericMethod<" + typeof(T) + ">");
}
//枚举类型
public TestEnum EnumTestFunc(TestEnum e)
{
Debug.Log("EnumTestFunc: e=" + e);
return TestEnum.E2;
}
public enum TestEnumInner
{
E3,
E4
}
//委托调用:下面是直接用lambda表达式实现了带有一个string类型参数的无返回值委托
public Action TestDelegate = (param) =>
{
Debug.Log("TestDelegate in c#:" + param);
};
//事件回调
//定义一个事件成员,在lua层那边可以通过特殊方式添加和删除事件回调,然后启动事件
public event Action TestEvent;
//启动事件的函数
public void CallEvent()
{
TestEvent();
}
//支持lua64位计算
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();
}
}
[LuaCallCSharp]
public interface ICalc
{
int add(int a, int b);
}
//扩展方法:在不改变类的情况下扩展类对象的方法
//实现多种类型的扩展方法,静态类,静态方法,参数为类类型前面需要加上this关键字,调用的时候使用参数类型的对象
[LuaCallCSharp] //必须加上标签,不然lua会找不到这个扩展方法类
public static class DrivenClassExtensions
{
public static int GetSomeData(this DrivenClass 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 DrivenClass obj)
{
obj.GenericMethod();
}
}
///
/// 研究普通类中的函数在lua中的调用
///
public class TestClass
{
[LuaCallCSharp]
public void Test()
{
Debug.Log("我是普通类(既不是静态类也继承于MonoBehaviour,也没有添加[LuaCallCSharp])中加了[LuaCallCSharp]的函数");
}
}
public class HelloXLua_LuaCallCSharp : MonoBehaviour {
LuaEnv luaenv = null;
//使用文本形式的代码代替Lua脚本
string script = @"
function demo()
--new出来的C#对象:也会出现在场景中,对象的名称没有给定就会显示默认的new GameObject
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('hello world',GameObject.Find('Main Camera')) --静态方法调用
--访问成员属性和方法:成员属性使用点号,成员方法使用冒号
local DrivenClass = CS.DrivenClass; --在实际项目中,可以先用一个局部变量将C#那边的对象引用一下,今后只需要使用这个变量就好了,避免多次映射访问C#那边的对象增加消耗
local testObj = DrivenClass() --创建一个对象时不需要new关键字
testObj.DMF = 1024 --设置成员属性使用类对象+点号
print(testObj.DMF) --调用成员属性使用类对象+点号
testObj:DMFunc() --调用成员方法使用类对象+冒号
--testObj.DMFunc() --成员方法使用点号访问会报错
--基类属性和方法
print(DrivenClass.BSF) --读取基类静态属性使用类名+点号
DrivenClass.BSF = 2048 --写基类静态属性使用类名+点号
DrivenClass.BSFunc() --调用基类静态方法使用类名+点号
print(testObj.BMF) --读基类成员属性使用类对象+点号
testObj.BMF = 4096 --写基类成员属性使用类对象+点号
testObj:BMFunc() --基类方法调用使用类对象+冒号
--复杂方法调用:接收多个返回值,如果C#函数有返回值那么这个返回值排在第一位,其余以ref和out方式返回的按照从左到右依次排序
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('haha')
--操作符
local testObj2 = DrivenClass()
testObj2.DMF = 2048
print('(testObj + testObj2).DMF = ',(testObj + testObj2).DMF)
--函数包含默认参数
testObj:DefaultValueFunc(1)
testObj:DefaultValueFunc(3,'hello','john')
--可变参数:调用的C#成员函数的参数是不固定的,lua这边也只需要传入对应类型的参数即可,也可以不传
testObj:VariableParamsFunc(5, 'hello', 'john')
--Extension methods扩展方法,扩展方法是静态方法,使用对象+冒号访问
print(testObj:GetSomeData())
print(testObj:GetSomeBaseData()) --访问基类的扩展方法
testObj:GenericMethodOfString() --通过扩展方法访问泛型方法
--枚举类型的操作:如果枚举类加入到生成代码的话,枚举类将支持__CastFrom方法,可以实现从一个整数或者字符串到枚举值的转换
local e = testObj:EnumTestFunc(CS.TestEnum.E1)
print(e, e == CS.TestEnum.E2)
print(CS.TestEnum.__CastFrom(1), CS.TestEnum.__CastFrom('E1'))
print(CS.DrivenClass.TestEnumInner.E3)
assert(CS.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 num = testObj:TestLong(11)
print(type(num),num,num+100,num+10000)
--typeof:使用typeof如以下的方式给一个Unity的GameObject对象添加组件
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)
print(calc.id)
calc.id = 50
print(calc.id)
cast(calc, typeof(CS.DrivenClass.InnerCalc)) --用CS.Calc的生成代码来访问calc对象,消耗减小
--cast(calc, typeof(CS.Calc)) --使用CS.Calc为目标转换类型,由于此类型里面没有id这个变量,所以calc.id的值会变为nil
print('cast to interface ICalc', calc:add(1, 2))
print(calc.id)
assert(calc.id ~= nil) --断言括号中的语句成立,如果不成立就报错
--继承MonoBehavior的类Lua中调用静态函数:可以直接CS.类名.静态函数名调用
CS.HelloXLua_LuaCallCSharp.TestLuaCallCS();
--继承MonoBehavior的类Lua中调普通成员函数,需要生成对象后调用,使用冒号
--以下两种方式都可以
--local testObj = CS.HelloXLua_LuaCallCSharp():TestLuaCallCS111();
local testObj = CS.HelloXLua_LuaCallCSharp();
testObj:TestLuaCallCS111();
--研究普通类中的函数在lua中的调用
CS.TestClass():Test();
end
--函数的普通调用
--demo()
--协程下使用
local co = coroutine.create(function()
print('------------------------------------------------------')
demo()
end)
assert(coroutine.resume(co))
";
public static void TestLuaCallCS()
{
print("继承于MonoBehaviour的类,可以直接CS.类名.静态函数名调用");
}
public void TestLuaCallCS111()
{
print("继承于MonoBehaviour的类,调用里面的普通成员函数需要生成对象后调用");
}
// Use this for initialization
void Start()
{
luaenv = new LuaEnv();
luaenv.DoString(script);
}
// Update is called once per frame
void Update()
{
if (luaenv != null)
{
//清除Lua未手动释放的LuaBase对象(这个操作是未处理过的,太频繁了,可以增加定时清理)
luaenv.Tick();
}
}
void OnDestroy()
{
luaenv.Dispose();
}
}