lua学习笔记

8 篇文章 0 订阅

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();
	}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值