Unity Lua 之 在 Unity 中 C#访问Lua 的全局变量、全局 table 和全局 function 的方法

 Unity Lua 之 在 Unity 中 C#访问Lua 的全局变量、全局 table 和全局 function 的方法

 

目录

 Unity Lua 之 在 Unity 中 C#访问Lua 的全局变量、全局 table 和全局 function 的方法

一、简单介绍

二、C#访问Lua 官网相关知识

三、注意事项:

四、实现步骤

五、关键代码


 

一、简单介绍

Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

xLua为Unity、 .Net、 Mono等C#环境增加Lua脚本编程的能力,借助xLua,这些Lua代码可以方便的和C#相互调用。

xLua在功能、性能、易用性都有不少突破,这几方面分别最具代表性的是:

  •         可以运行时把C#实现(方法,操作符,属性,事件等等)替换成lua实现;
  •         出色的GC优化,自定义struct,枚举在Lua和C#间传递无C# gc alloc;
  •         编辑器下无需生成代码,开发更轻量;

在Unity中使用xlua 的重要一个原因就是热更新,我们本着这个歌目的开始我们的学习。

 

二、C#访问Lua 官网相关知识

这里指的是C#主动发起对Lua数据结构的访问。 本章涉及到的例子都可以在XLua\Tutorial\CSharpCallLua下找到。

  1. 获取一个全局基本数据类型 访问LuaEnv.Global就可以了,上面有个模版Get方法,可指定返回的类型。

     luaenv.Global.Get<int>("a")
     luaenv.Global.Get<string>("b")
     luaenv.Global.Get<bool>("c")
    
  2. 访问一个全局的table

    也是用上面的Get方法,那类型要指定成啥呢?

    1. 映射到普通class或struct

      定义一个class,有对应于table的字段的public属性,而且有无参数构造函数即可,比如对于{f1 = 100, f2 = 100}可以定义一个包含public int f1;public int f2;的class。 这种方式下xLua会帮你new一个实例,并把对应的字段赋值过去。

      table的属性可以多于或者少于class的属性。可以嵌套其它复杂类型。 要注意的是,这个过程是值拷贝,如果class比较复杂代价会比较大。而且修改class的字段值不会同步到table,反过来也不会。

      这个功能可以通过把类型加到GCOptimize生成降低开销,详细可参见配置介绍文档。 那有没有引用方式的映射呢?有,下面这个就是:

    2. 映射到一个interface

      这种方式依赖于生成代码(如果没生成代码会抛InvalidCastException异常),代码生成器会生成这个interface的实例,如果get一个属性,生成代码会get对应的table字段,如果set属性也会设置对应的字段。甚至可以通过interface的方法访问lua的函数。

    3. 更轻量级的by value方式:映射到Dictionary<>,List<>

      不想定义class或者interface的话,可以考虑用这个,前提table下key和value的类型都是一致的。

    4. 另外一种by ref方式:映射到LuaTable类

      这种方式好处是不需要生成代码,但也有一些问题,比如慢,比方式2要慢一个数量级,比如没有类型检查。

  3. 访问一个全局的function

    仍然是用Get方法,不同的是类型映射。

    1. 映射到delegate

      这种是建议的方式,性能好很多,而且类型安全。缺点是要生成代码(如果没生成代码会抛InvalidCastException异常)。

      delegate要怎样声明呢? 对于function的每个参数就声明一个输入类型的参数。 多返回值要怎么处理?从左往右映射到c#的输出参数,输出参数包括返回值,out参数,ref参数。

      参数、返回值类型支持哪些呢?都支持,各种复杂类型,out,ref修饰的,甚至可以返回另外一个delegate。

      delegate的使用就更简单了,直接像个函数那样用就可以了。

    2. 映射到LuaFunction

      这种方式的优缺点刚好和第一种相反。 使用也简单,LuaFunction上有个变参的Call函数,可以传任意类型,任意个数的参数,返回值是object的数组,对应于lua的多返回值。

  4. 使用建议

    1. 访问lua全局数据,特别是table以及function,代价比较大,建议尽量少做,比如在初始化时把要调用的lua function获取一次(映射到delegate)后,保存下来,后续直接调用该delegate即可。table也类似。

    2. 如果lua测的实现的部分都以delegate和interface的方式提供,使用方可以完全和xLua解耦:由一个专门的模块负责xlua的初始化以及delegate、interface的映射,然后把这些delegate和interface设置到要用到它们的地方。

 

三、注意事项:

1、获取 Lua 中的全局变量的时候,注意数据类型要对上,不然可能获取不到正确的全局变量数据

2、在使用 class 获取 table 的时候,好像获取不到里面的函数(不知道就是这样,还是能获取到,只是方法不对)

3、在使用 interface 获取 table 的时候,注意 在 interface 前添加上 public(例如 public interface IPerson{}),不然可能会报访问不到

4、在使用 class 获取 table 的时候,修改值不会映射会lua中;而在使用 interface 获取 table 的时候,修改值会映射会lua中

5、注意 xlua (版本2.1.14)实话也不是很兼容 Unity 2018.4.12f1 调用 [CSharpCallLua] 会报错,报找不到

(如果你也有这样可以使用 Unity 2017.4.0f1 版本,测试可用)

6、使用 Delegate 获取 Lua 全局函数的时候,可以使用 ref 和 out  接受 lua 中多余的返回值

7、获取全局函数的时候,获取的函数变量, 函数变量使用完建议置空,需要反复使用,可以最后在把函数 = null

 

四、实现步骤

1、新建一个文本文件 CSharpGetLuaGlobalParamFunc.lua.txt

 

2、打开编写 lua 代码

 

3、新建 Unity 工程,新建一个 LuaTxt 文件夹,并把 文本文件 CSharpGetLuaGlobalParamFunc.lua.txt 导入

 

4、在工程中,新建脚本实现 C# 访问 Lua 的功能

 

5、把脚本挂载到场景中

 

6、运行场景,效果如下,获取到 Lua 中的属性和方法

 

五、关键代码

1、CSharpGetLuaGlobalParamFunc.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;

public class CSharpGetLuaGlobalParamFunc : MonoBehaviour
{
    // Lua 环境变量参数
    LuaEnv luaEnv;

    // Start is called before the first frame update
    void Start()
    {
        LuaInit();

        luaEnv.DoString("require'CSharpGetLuaGlobalParamFunc'");

        GetLuaGlobalParameters();
        GetLuaGlobalTable_Method_Class();
        GetLuaGlobalTable_Method_Interface();
        GetLuaGlobalTable_Method_Dictionary_List();
        GetLuaGlobalFunction_Method_Delegate();
        GetLuaGlobalFunction_Method_LuaFunction();
    }

    /// <summary>
    /// 获取 Lua 全局变量
    /// </summary>
    void GetLuaGlobalParameters() {

        // 一定要注意类型,不然可能获取不到正确的值哈
        float numberFloat = luaEnv.Global.Get<float>("numberFloat");
        int numberInt = luaEnv.Global.Get<int>("numberInt");
        string stringData = luaEnv.Global.Get<string>("stringData");
        bool isTrue = luaEnv.Global.Get<bool>("isTrue");

        Debug.Log("numberFloat:" + numberFloat);
        Debug.Log("numberInt:" + numberInt);
        Debug.Log("stringData:" + stringData);
        Debug.Log("isTrue:" + isTrue);
    }


    #region Get Table Class
    /// <summary>
    /// 使用 Class 获取 Table 的内容
    /// 前提:定义一个和 Table 对应的 Class 类
    /// (好似 函数获取不到)
    /// </summary>
    void GetLuaGlobalTable_Method_Class() {

        Person person = luaEnv.Global.Get<Person>("person");
        Debug.Log("GetLuaGlobalTable_Method_Class :");
        Debug.Log("name:" + person.name + " age:" + person.age);
        // 修改的值不会映射到 lua 里面
        person.age = 20;
        luaEnv.DoString("print(person.age)");
        // class 方法获取好似获取不到函数
        person.work(1, 2);
    }

    /// <summary>
    /// 与 Lua table 对应的 class 类
    /// </summary>
    class Person
    {
        public string name;
        public int age;
        public void work(float a, float b) { }
    }

    #endregion

    #region Get Table Interface

    /// <summary>
    /// 与 Lua table 对应的 interface,请加上[CSharpCallLua] 修饰,并设置为 public 属性
    /// </summary>
    [CSharpCallLua]
    public interface IPerson
    {
        string name { get; set; }
        int age { get; set; }
        void work(float a, float b);
    }

    /// <summary>
    /// 使用 Class 获取 Table 的内容
    /// 前提:定义一个和 Table 对应的 interface 类
    /// </summary>
    void GetLuaGlobalTable_Method_Interface()
    {
        IPerson person = luaEnv.Global.Get<IPerson>("person");
        Debug.Log("GetLuaGlobalTable_Method_Interface :");
        Debug.Log("name:" + person.name + " age:" + person.age);
        // 修改的值会映射到 lua 里面
        person.age = 20;
        luaEnv.DoString("print(person.age)");
        // 执行函数
        person.work(1, 2);
        
    }

    #endregion

    #region Get Table Dictionary、List
    /// <summary>
    /// 使用 Dictionary、List 获取 Table 内容
    /// </summary>
    void GetLuaGlobalTable_Method_Dictionary_List() {

        // 获取有变量的值
        Dictionary<string, object> dic = luaEnv.Global.Get<Dictionary<string, object>>("person");
        Debug.Log("Dictionary:");
        foreach (string key in dic.Keys)
        {
            Debug.Log(key + ":" + dic[key]);
        }

        // 获取到 单一的值
        List<object> list = luaEnv.Global.Get<List<object>>("person");
        Debug.Log("List:");
        foreach (object item in list)
        {
            Debug.Log(item);
        }

    }
    #endregion

    #region Get Function Delegate

    /// <summary>
    /// 定义与lua 全局函数 对应的 delegate
    /// </summary>
    /// <param name="a"></param>
    /// <param name="b"></param>
    /// <param name="out_a"></param>
    /// <param name="out_b"></param>
    /// <returns></returns>
    [CSharpCallLua]
    public delegate float Sub(float a, float b, out float out_a, out float out_b);

    /// <summary>
    /// 使用 Delegate 获取 Lua 全局函数
    /// 前提:定义一个与之对应的 delegate
    /// </summary>
    void GetLuaGlobalFunction_Method_Delegate() {
        Debug.Log("GetLuaGlobalFunction_Method_Delegate:");
        Sub sub = luaEnv.Global.Get<Sub>("sub");
        float out_a, out_b;

        // 可以使用 ref 和 out  接受 lua 中多余的返回值
        float ret = sub(3.7f, 1.9f, out out_a, out out_b);
        Debug.LogFormat("ret:{0},out_a:{1},out_b:{2}", ret, out_a, out_b);

        // 函数使用完建议置空,需要反复使用,可以最后在把函数 = null
        sub = null;

    }
    #endregion

    #region Get Function LuaFunction
    /// <summary>
    /// 使用 LuaFunction 获取 lua 全局函数
    /// </summary>
    void GetLuaGlobalFunction_Method_LuaFunction(){
        Debug.Log("GetLuaGlobalFunction_Method_LuaFunction:");
        LuaFunction sub = luaEnv.Global.Get<LuaFunction>("sub");
        object[] rets = sub.Call(4,6);
        foreach (var item in rets)
        {
            Debug.Log(item);
        }

        // 函数使用完建议置空,需要反复使用,可以最后在把函数 = null
        sub = null;
    }


    #endregion

    void OnDestroy() {
        LuaDispose();
    }
    
    /// <summary>
    /// Lua 环境初始化
    /// </summary>
    private void LuaInit() {
        luaEnv = new LuaEnv();
    }

    /// <summary>
    /// Lua 环境释放
    /// </summary>
    private void LuaDispose() {
        luaEnv.Dispose();
    }

    
}

 

2、CSharpGetLuaGlobalParamFunc.lua.txt

numberFloat = 20.05
numberInt = 5
stringData = "Hello Lua"
isTrue = false

person={
	name = "Lua",
	age = 18,
	work=function(self,a,b)
		print("Lua 工作:" + (a+b))
	end
}

function sub(a,b)
	print(a-b)
	return a-b,a,b
end

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

仙魁XAN

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值