ToLua 热更新框架

0.参考文章

  1. 【游戏开发高阶】从零到一教你Unity使用ToLua实现热更新(含Demo工程 | LuaFramework | 增量 | HotUpdate)
  2. 浅探ToLua框架
  3. ToLua API使用
  4. tolua之wrap文件的原理与使用
  5. 推荐:https://www.cnblogs.com/movin2333/p/14639142.html

1.框架基本原理

ToLua 框架主要是通过静态绑定来实现 C# 与 Lua 之间的交互的,基本原理是通过建立一个 Lua 虚拟机来映射 C# 脚本,然后再通过这个虚拟机来运行 Lua 脚本,Lua 脚本在运行时可以通过虚拟机反过来调用 C# 脚本里注册过的物体,虚拟机变相地实现了一个解释器的功能,这种方式的优势在于比起使用反射的 uLua 来说效率更高。

2.下载和安装

github地址:https://github.com/topameng/tolua
安装:在这里插入图片描述
将以上三个文件夹保持目录结构导入到unity工程中

3.使用

可查看Assets\ToLua\Examples中的例子

3.1 C#调用 Lua 代码

3.1.1 创建虚拟机

创建虚拟机和启动虚拟机之后,就可以调用 Lua 代码

LuaState state = new LuaState();	// 创建虚拟机
state.Start();	// 启动虚拟机

3.1.2 执行一段 Lua 代码

  • state.DoString(string chunk, string chunkName = “LuaState.cs”):比较少用这种方式加载代码,无法避免代码重复加载覆盖等,需调用者自己保证。第二个参数用于调试信息,或者 error 消息,用于提示出错代码所在文件名称。
  • state.DoFile(string fileName):加载一个 lua 文件,fileName 需要扩展名(.lua),可反复执行,后面的变量会覆盖之前的 DoFile 加载的变量。
  • state.Require(string fileName):同 lua require(modname),会判断是否文件已经加载避免重复加载同一文件
  • 注意: 使用 DoFile 和 Require 方法时,要用state.AddSearchPath(string fullPath) 手动给目标文件添加一个文件搜索位置。

示例:

LuaState state = new LuaState();
state.Start();
string hello =
    @"                
        print('hello tolua#')                                  
    ";
state.DoString(hello, "HelloWorld.cs");

//如果移动了ToLua目录,需要自己手动,这里只是例子就不做配置了
string fullPath = Application.dataPath + "/ToLua/Examples/02_ScriptsFromFile";
state.AddSearchPath(fullPath);  
state.DoFile("ScriptsFromFile.lua"); 
state.Require("ScriptsFromFile"); 

// 垃圾回收, 对于被自动 gc 的 LuaFunction, LuaTable, 以及委托减掉的 LuaFunction, 延迟删除的 Object 之类
// 等等需要延迟处理的回收, 都在这里自动执行
state.Collect();
// 检查是否堆栈是否平衡,一般放于 update 中,c# 中任何使用 lua 堆栈操作,
// 都需要调用者自己平衡堆栈(参考 LuaFunction 以及 LuaTable 代码), 
// 当 CheckTop 出现警告时其实早已经离开了堆栈操作范围,这时需自行 review 代码
state.CheckTop();
// 释放LuaState 以及其资源
state.Dispose();
state= null;

3.1.3 调用 Lua 变量/函数

3.1.3.1 获取 Lua 变量/函数

state[string]:通过 LuaState [string] 的形式就可以直接获取到,也可以通过这个表达式来直接赋值,取到的是 object 类型。

  • state[“变量名”]:通过这种方式对 lua 中的变量进行访问
  • state[“方法名”] as LuaFunction:LuaState 对象 转 LuaFunction,调用 lua 中的函数
  • GetFunction(“test.luaFunc”):获取lua中指定test表的luaFunc函数
3.1.3.2 调用 Lua 函数
  • state.Invoke():临时调用一个 lua function 并返回一个值,这个操作并不缓存 lua function,适合频率非常低的函数调用,可带参数。
  • state.Call():看起来和 Invoke 类似,只是无返回值。
  • state.GetFunction(string): 获取并缓存一个 lua 函数,string 支持串式操作。
  • LuaFunction.Call():不需要返回值的函数调用操作。
  • LuaFunction.Invoke():有一个返回值的函数调用操作。
  • LuaFunction.BeginPCall():开始函数调用。
  • LuaFunction.Push():压入函数调用需要的参数。
  • LuaFunction.PCall():调用 lua 函数。
  • LuaFunction.CheckNumber():提取函数返回值, 并检查返回值为 lua number 类型。
    * LuaFunction.EndPCall():结束 lua 函数调用,清楚函数调用造成的堆栈变化。
  • luaFunc.ToDelegate:创建一个委托,后续直接调用委托即可。
  • LuaFunction.Dispose():释放 LuaFunction,递减引用计数,如果引用计数为 0,则从 _R 表删除该函数。
private string script =
@"  function luaFunc(num)                        
        return num + 1
    end
";

void Start () 
{
    
    
    LuaState state = new LuaState();
    state.Start();
    DelegateFactory.Init();
    state.DoString(script);

    state.Call<int>("luaFunc", 123456, false);
    Debugger.Log("state.Call");

    int num = state.Invoke<int, int>("luaFunc", 123456, false);
    Debugger.Log("state.Invoke: {0}", num);	// 123457

    LuaFunction luaFunc = state.GetFunction("luaFunc");
    if (luaFunc != null)
    {
    
    
        luaFunc.Call<int>(123456);
        Debugger.Log("LuaFunction.Call");

        num = luaFunc.Invoke<int, int>(123456);
        Debugger.Log("LuaFunction.Invoke: {0}", num);		// 123457

        luaFunc.BeginPCall();                
        luaFunc.Push(123456);
        luaFunc.PCall(); 
        num = (int)luaFunc.CheckNumber();                    
        luaFunc.EndPCall();
        Debugger.Log("LuaFunction.PCall: {0}", num);	// 123457
        
        Func<int, int> func = luaFunc.ToDelegate<Func<int, int>>();
        num = func(123456);
        Debugger.Log("LuaFunction.ToDelegate: {0}", num);	// 123457
    }
            
    state.CheckTop();
    luaFunc.Dispose();
    luaFunc = null;
    state.Dispose();
    state = null;
}

3.1.4 ToLua table 方法

这里完全可以将 table 看一个 LuaState 来进行操作,ToLua 对 table 的数据结构进行了解析,实现了非常多的方法:

  • state[“变量名”]:state通过重载this操作符,访问lua _G表中的变量
  • state.GetTable(string):从 state 中获取一个 table, 可以串式访问,比如:state.GetTable(“varTable.map.name”) 等于 varTable->map->name
  • LuaTable 支持this操作符,但此this不支持串式访问。比如:table[“map.name”] ,“map.name” 只是一个key,不是table->map->name 。
  • table.GetMetaTable():可以获取当前 table 的 metatable。
  • table.ToArray():获取数组表中的所有对象存入到object[]表中。
  • table.AddTable(name):在当前的table表中添加一个名字为name的表。
  • table.GetTable(key):获取 table[key] 值,类似于 lua_gettable。
  • table.SetTable(key, value):等价于 table[k] = v,类似于lua_settable。
  • table.RawGet(key):获取 table[key]值,类似于 lua_rawget
  • table.RawSet(key, value):等价于 table[k] = v,类似于lua_rawset。
private string script =
    @"
        print('Objs2Spawn is: '..Objs2Spawn)
        var2read = 42
        varTable = {1,2,3,4,5}
        varTable.default = 1
        varTable.map = {}
        varTable.map.name = 'map'
        
        meta = {name = 'meta'}
        setmetatable(varTable, meta)
        
        function TestFunc(strs)
            print('get func by variable')
        end
    ";

void Start () 
{
    
    
    LuaState state = new LuaState();
    state.Start();
    state["Objs2Spawn"] = 5;
    state.DoString(script);

    //通过LuaState访问
    Debugger.Log("Read var from lua: {0}", state["var2read"]);	// 42
    Debugger.Log("Read table var from lua: {0}", state["varTable.default"]);  // 1 

    //cache成LuaTable进行访问
    LuaTable table = state.GetTable("varTable");
    // table["map.name"]:不支持串式访问
    Debugger.Log("Read varTable from lua, default: {0} name: {1}", table["default"], table["map.name"]); // default: 1 name:
    table["map.name"] = "new";  //table 字符串只能是key
    Debugger.Log("Modify varTable name: {0}", table["map.name"]);	// new

    table.AddTable("newmap");
    LuaTable table1 = (LuaTable)table["newmap"];
    table1["name"] = "table1";
    Debugger.Log("varTable.newmap name: {0}", table1["name"]);	// table1
    table1.Dispose();

    table1 = table.GetMetaTable();
    if (table1 != null)
    {
    
    
        Debugger.Log("varTable metatable name: {0}", table1["name"]); // meta
    }

    object[] list = table.ToArray();
    for (int i = 0; i < list.Length; i++)
    {
    
    
        Debugger.Log("varTable[{0}], is {1}", i, list[i]);	// 1, 2, 3, 4, 5
    }

    table.Dispose();                        
    state.CheckTop();
    state.Dispose();
}

3.2 Lua 调用C#代码

3.2.1 Lua 中使用 coroutine(携程)

使用Lua携程必须添加LuaLooper脚本

  • coroutine.start:启动一个lua携程
  • coroutine.wait:携程中等待一段时间,单位:秒
  • coroutine.step:携程中等待一帧.
  • coroutine.www:等待一个WWW完成.
  • tolua.tolstring:转换byte数组为lua字符串缓冲
  • coroutine.stop:停止一个正在lua将要执行的携程

Lua脚本:

-------tolua官方案例:初始化时,C#调用此函数开启协程----------
function TestCortinue()	
    coroutine.start(CoFunc)
end

local coDelay = nil

------------------------每隔1秒打印一次输出------------------
function Delay()
	local c = 1

	while true do
		coroutine.wait(1) 
		print("Count: "..c)
		c = c + 1
	end
end

----------------------------开启协程------------------------
function StartDelay()
	coDelay=coroutine.start(Delay)
end

----------------------------停止协程------------------------
function StopDelay()
	coroutine.stop(coDelay)
end

----------------------------每隔0.1秒计算一次------------------------
function fib(n)
    local a, b = 0, 1
    while n > 0 do
        a, b = b, a + b
        n = n - 1
    end

    return a
end

----------------------------协程运行函数------------------------
function CoFunc()
    print('Coroutine started')    
    for i = 0, 10, 1 do
        print(fib(i))                    
        coroutine.wait(0.1)--等待0.1秒						
    end	
	print("current frameCount: "..Time.frameCount)
	coroutine.step()--等待一帧
	print("yield frameCount: "..Time.frameCount)

	local www = UnityEngine.WWW("http://www.baidu.com")
	coroutine.www(www)--开启www请求
	local s = tolua.tolstring(www.bytes)
	print(s:sub(1, 128))
    print('Coroutine ended')
end

C#脚本:

new LuaResLoader();
lua  = new LuaState();
lua.Start();
LuaBinder.Bind(lua);							// 绑定lua对象
DelegateFactory.Init();  						// 委托工厂初始化       
looper = gameObject.AddComponent<LuaLooper>();	// 使用协程必须添加
looper.luaState = lua;							// 指定对象

lua.DoString(luaFile.text, "TestLuaCoroutine.lua");
LuaFunction f = lua.GetFunction("TestCortinue");
f.Call();
f.Dispose();
f = null;  

3.2.1 Lua 中使用数组

  • func.Push(arrayInts) :将 数组 arrayInts 传至 Lua 中
  • func.CheckNumber():得到 Lua 对应函数返回值中的:值类型
  • func.CheckString():得到 Lua 对应函数返回值中的:字符串类型
  • func.CheckBoolean():得到 Lua 对应函数返回值中的:布尔类型
    注意: 书写顺序应该是与函数返回值先后顺序保持一致,否则会报错
    Lua代码:
 private string script =
        @"
			function TestArray(array)
			----------------------------获取数组长度------------------------
			    local len = array.Length
			----------------------------遍历数组输出------------------------
			    for i = 0, len - 1 do
			        print('数组: '..tostring(array[i]))
			    end
			----------------------------获取枚举器--------------------------
			    local iter = array:GetEnumerator()
			----------------------调用数组函数MoveNext()--------------------
			    while iter:MoveNext() do 
			        print('依次取: '..iter.Current)
			    end
			----------------------数组转表----------------------------------
			    local t = array:ToTable()
			    for i = 1, #t do
			        print('转表: ' .. tostring(t[i]))
			    end
			----------------------调用数组函数BinarySearch(3)---------------
			    local pos = array:BinarySearch(3)
			    print('数组二进制 BinarySearch: 下标是: '..pos..' 值: '..array[pos])
			----------------------调用数组函数IndexOf(4)--------------------
			    pos = array:IndexOf(4)
			    print('数组 indexof 4 下标是: '..pos)
			----------------------返回值参数--------------------------------
			    return 5, 'Chinar', true
			end
		"
————————————————

C#代码:

LuaState    lua  = null;
LuaFunction func = null;
string      tips = null;

void Start()
{
    
    
    new LuaResLoader();
    tips = "";

    lua = new LuaState();
    lua.Start();
    lua.DoString(script, "ChinarTestArray.cs"); //运行 script 字符串中的lua脚本,指定C#脚本路径

    int[] arrayInts = {
    
    1, 2, 3, 4, 5};
    func            = lua.GetFunction("TestArray"); //取得 lua script 脚本中的 TestArray 函数
    func.BeginPCall();

    #region 第一种写法

    func.Push(arrayInts);
    func.PCall();
    //  接受返回值时顺序要一一对应。 顺序写错,会报错
    //  另外 int string float 这些会自动完成转换,不会报错
    //  如: 10.3 ——  func.CheckValua<int>(); 这里会自动 返回 int
    //  如: 1 ——  func.CheckNumber();
    //  如: `123` ——  func.CheckString();
    //  如: true ——  func.CheckBoolean();
    double arg1 = func.CheckNumber();
    string arg2 = func.CheckString();
    bool   arg3 = func.CheckBoolean();

    Debugger.Log("返回值:{0}|{1}|{2}", arg1, arg2, arg3);

    //-------------------------------------------
    func.EndPCall();

    #endregion


    #region 第二种通用写法

    //调用通用函数需要转换一下类型,避免可变参数拆成多个参数传递
    //func.LazyCall((object) arrayInts);
    //相当于 ↓↓
    //func.Push(arrayInts);
    //func.PCall();
    object[] objects = func.LazyCall((object) arrayInts);
    if (objects != null)
    {
    
    
        Debugger.Log("return is {0} {1} {2}", objects[0], objects[1], objects[2]);
    }
    func.EndPCall();

    lua.CheckTop(); //检查返回值

    #endregion
}
————————————————

3.2.2 Lua 中使用字典

map.Add(1, new TestAccount(1, “水水”, 0)):字典中添加元素:利用构造函数,新建测试账号对象时,在对象中直接完成赋值

注意:
这对于初学者是一个老生常谈的问题了
Lua 中调用C#函数用“:”,字段用“.”
: —— map:GetEnumerator()/iter:MoveNext()
. ——iter.Current.name /map.Keys

C#脚本:

using UnityEngine;
using System.Collections.Generic;
using LuaInterface;


/// <summary>
/// Chinar解释 —— tolua官方案例9
/// Lua中使用字典
/// </summary>
public sealed class TestAccount
{
    
    
    public int    id;
    public string name;
    public int    sex;


    /// <summary>
    /// 构造函数
    /// </summary>
    public TestAccount(int id, string name, int sex)
    {
    
    
        this.id   = id;
        this.name = name;
        this.sex  = sex;
    }
}

public class UseDictionary : MonoBehaviour
{
    
    
    private Dictionary<int, TestAccount> map = new Dictionary<int, TestAccount>();

    string script =
        @"              
        function TestDict(map)

        ------------调用字典函数GetEnumerator()---------------
            local iter = map:GetEnumerator()

        -------------调用字典函数MoveNext()-------------------
            while iter:MoveNext() do
                local v = iter.Current.Value
                print('id: '..v.id .. ' name: '..v.name..' sex: '..v.sex)
            end
        -------------调用字典函数TryGetValue()-----------------
            local flag, account = map:TryGetValue(1, nil)
    
            if flag then
                print('TryGetValue 返回结果正确时: '..account.name)
            end

        --------------------获取字典中的键---------------------
            local keys = map.Keys
            iter = keys:GetEnumerator()
            print('------------打印输出字典中的键---------------')
            while iter:MoveNext() do
                print(iter.Current)
            end
            print('----------------------over----------------------')
    
            local values = map.Values
            iter = values:GetEnumerator()
            print('------------打印输出字典中的值---------------')
            while iter:MoveNext() do
                print(iter.Current.name)
            end
            print('----------------------over----------------------')

        -----------获取字典中2键对应的name值---------------------
            print('kick '..map[2].name)

        -----------移除map中下标2对应的元素---------------------
            map:Remove(2)
            iter = map:GetEnumerator()
    
        -----------重新输出---------------------
            while iter:MoveNext() do
                local v = iter.Current.Value
                print('id: '..v.id .. ' name: '..v.name..' sex: '..v.sex)
            end
        end
                        
        ";


    void Awake()
    {
    
    
#if UNITY_5 || UNITY_2017 || UNITY_2018
        Application.logMessageReceived += ShowTips;
#else
        Application.RegisterLogCallback(ShowTips);
#endif
        new LuaResLoader();
        map.Add(1, new TestAccount(1, "水水", 0));
        map.Add(2, new TestAccount(2, "王伟", 1));
        map.Add(3, new TestAccount(3, "王芳", 0));
        LuaState luaState = new LuaState();
        luaState.Start();
        BindMap(luaState);

        luaState.DoString(script, "UseDictionary.cs");
        LuaFunction func = luaState.GetFunction("TestDict");
        func.BeginPCall();
        func.Push(map);
        func.PCall();
        func.EndPCall();

        func.Dispose();
        func = null;
        luaState.CheckTop();
        luaState.Dispose();
        luaState = null;
    }


    void OnApplicationQuit()
    {
    
    
#if UNITY_5 || UNITY_2017 || UNITY_2018
        Application.logMessageReceived -= ShowTips;
#else
        Application.RegisterLogCallback(null);
#endif
    }


    string tips = "";


    void ShowTips(string msg, string stackTrace, LogType type)
    {
    
    
        tips += msg;
        tips += "\r\n";
    }


    void OnGUI()
    {
    
    
        GUI.Label(new Rect(Screen.width / 2 - 300, Screen.height / 2 - 200, 600, 400), tips);
    }


    //示例方式,方便删除,正常导出无需手写下面代码
    //Chinar:这里不用管,这是tolua作者为方便我们删除示例预留的代码
    //为了保证删除示例是,不影响我们的整体工程的其他 wrap 文件
    void BindMap(LuaState L)
    {
    
    
        L.BeginModule(null);
        TestAccountWrap.Register(L);
        L.BeginModule("System");
        L.BeginModule("Collections");
        L.BeginModule("Generic");
        System_Collections_Generic_Dictionary_int_TestAccountWrap.Register(L);
        System_Collections_Generic_KeyValuePair_int_TestAccountWrap.Register(L);
        L.BeginModule("Dictionary");
        System_Collections_Generic_Dictionary_int_TestAccount_KeyCollectionWrap.Register(L);
        System_Collections_Generic_Dictionary_int_TestAccount_ValueCollectionWrap.Register(L);
        L.EndModule();
        L.EndModule();
        L.EndModule();
        L.EndModule();
        L.EndModule();
    }
}

3.2.3 Lua 中使用枚举类型

LuaBinder.Bind(lua):首先这一步不能少
lua[“space”] = Space.World:Lua 脚本中的 space 变量,进行枚举类型赋值
C#代码:

using UnityEngine;
using LuaInterface;

/// <summary>
/// Chinar解释 —— tolua官方案例 10
/// Lua中使用Unity枚举类型
/// </summary>
public class AccessingEnum : MonoBehaviour
{
    
    
    string script =
        @"
            space = nil

            function TestEnum(e)
                print('枚举是:'..tostring(e))
                ----------------枚举类型转int----------------
                if space:ToInt() == 0 then
                    print('枚举转Int成功')
                end
    
                -------------枚举类型int与0比较--------------
                if not space:Equals(0) then
                    print('枚举类型(与int型)比较成功')
                end
    
                -------------枚举类型直接比较----------------
                if space == e then
                    print('枚举类型比较成功')
                end
 
                -------调用Unity中的Space枚举值转枚举--------
                local s = UnityEngine.Space.IntToEnum(0)
    
                if space == s then
                    print('int转枚举,比较成功')
                end
            end
 
            ----------------改变Unity光照类型---------------
            function ChangeLightType(light, type)
                print('改变光照类型为: '..tostring(type))
                light.type = type
            end
        ";

    LuaState state = null;
    string   tips  = "";
    int      count = 1;


    /// <summary>
    /// 格式化打印输出
    /// </summary>
    void ShowTips(string msg, string stackTrace, LogType type)
    {
    
    
        tips += msg;
        tips += "\r\n";
    }


    /// <summary>
    /// 初始化函数
    /// </summary>
    void Start()
    {
    
    
#if UNITY_5 || UNITY_2017 || UNITY_2018
        Application.logMessageReceived += ShowTips;
#else
        Application.RegisterLogCallback(ShowTips);
#endif
        new LuaResLoader();
        state = new LuaState();
        state.Start();
        LuaBinder.Bind(state); //绑定,必须要写

        state.DoString(script);
        state["space"] = Space.World;  //Lua脚本中的space变量,进行枚举类型赋值

        LuaFunction func = state.GetFunction("TestEnum");
        func.BeginPCall();
        func.Push(Space.World);
        func.PCall();
        func.EndPCall();
        func.Dispose();
        func = null;
    }


    /// <summary>
    /// 绘制函数
    /// </summary>
    void OnGUI()
    {
    
    
        GUI.Label(new Rect(Screen.width / 2 - 300, Screen.height / 2 - 200, 600, 400), tips);

        if (GUI.Button(new Rect(0, 60, 120, 50), "ChangeType"))
        {
    
    
            Light       light = GameObject.Find("/Light").GetComponent<Light>(); //获取层次列表中对象的Light组件
            LuaFunction func  = state.GetFunction("ChangeLightType");
            func.BeginPCall();
            func.Push(light);                           //将light传入ChangeLightType函数
            LightType type = (LightType) (count++ % 4); //简单计算得到类型
            func.Push(type);                            //将type类型传入ChangeLightType函数
            func.PCall();                               //调用ChangeLightType函数
            func.EndPCall();
            func.Dispose();
        }
    }


    /// <summary>
    /// 应用退出时执行函数
    /// </summary>
    void OnApplicationQuit()
    {
    
    
        state.CheckTop();
        state.Dispose();
        state = null;

#if UNITY_5 || UNITY_2017 || UNITY_2018
        Application.logMessageReceived -= ShowTips;
#else
        Application.RegisterLogCallback(null);
#endif
    }
}

3.3 Lua 调用自定义C#类中方法

推荐文章https://www.cnblogs.com/movin2333/p/14639142.html

  1. 新建供Lua调用的C#类和Mono类
    C#类:
public class CsScript
{
    
    
    public int idx;

    //  int Idx { get => idx; set => idx = value; }

    public CsScript() {
    
     }

    public CsScript(int idx)
    {
    
    
        this.idx = idx;
    }

    public int Sum(int a, int b)
    {
    
    
        return a + b;
    }
}

Mono类:

public class MomoCsScript : MonoBehaviour
{
    
    
    // Start is called before the first frame update
    void Start()
    {
    
    
        Debug.Log("Statr_ming");
    }

    // Update is called once per frame
    void Update()
    {
    
    
        
    }
}
  1. 在CustomSettings中注册
    在这里插入图片描述
    在这里插入图片描述
  2. 生成Warp文件
    在这里插入图片描述
    在这里插入图片描述
  3. 编写主入口代码
public class Enter : MonoBehaviour
{
    
    
    LuaState lua = null;
    LuaFunction luaFunc = null;
    // Start is called before the first frame update
    void Awake()
    {
    
    
        new LuaResLoader();
        lua = new LuaState();
        lua.Start();
        LuaBinder.Bind(lua);
        string luaPath = Application.dataPath + "/Scripts";//注意这里的文件位置
        lua.AddSearchPath(luaPath);
        lua.DoFile("TestLuaCallCS.lua");
        CallFunc("TestLuaCallCS.Awake", this.gameObject);
    }

    private void OnApplicationQuit()
    {
    
    
        lua.Dispose();
        lua = null;
    }

    void CallFunc(string func, GameObject obj)
    {
    
    
        luaFunc = lua.GetFunction(func);
        luaFunc.Call(obj);
        luaFunc.Dispose();
        luaFunc = null;
    }
}
  1. 编写Lua代码
    在这里插入图片描述
TestLuaCallCS = {
    
    };
local this = TestLuaCallCS;
local GameObject = UnityEngine.GameObject
local AudioSource = UnityEngine.AudioSource
local test = CsScript(18)

function this.Awake(object)
    local go = GameObject.Find('GameObject')
    go:AddComponent(typeof(AudioSource));
    go:AddComponent(typeof(MomoCsScript));
    print(test:Sum(1, 2))
    print(test.idx)
    print(go.name)
end
  1. 结果
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_45136016/article/details/142955409