Unity 初次使用ulua,熟悉C#和Lua交互

说明

一直都想实现Unity的热更新,但因为种种原因搁置了。前段时间学习了一下Lua的基本语法,发现这门语言还真是简单易学。于是实现Unity热更新的想法又开始蠢蠢欲动了…
看了一些大佬们的博客,发现C#和Lua之间交互的框架还挺多,思来想去最后决定使用ulua框架。
选择ulua框架的原因是它全平台支持,而且通过LuaJit进行加速,效率上不会太低,而且使用简单,导入package就可以编写代码了。不过ulua的作者只提供了ulua的插件包,并没有提供整套插件源码,但对于我这种新手来说也没什么影响…反正源码也看不懂,好用就行哈哈哈哈哈哈
我主要参考的是这篇博客,整理得非常详细——[整理]Unity3D游戏开发之Lua

下载

首先,当然要先下载ulua包,我实在这个网站上下载的——链接
这个网站里面的网盘链接已经失效了,我是通过GitHub下载的
选择GitHub
附上GitHub地址——链接
不知道是不是网络的问题,download总是失败,所以最后我选择拷贝HTTPS链接然后Clone
选择Clone
Clone完成后,有这些东西:
Clone完成

导入

新建或者打开一个Unity项目
打开Unity项目文件夹所在位置,原封不动地将下载下来的所有文件拷贝到了项目目录下
拷贝然后会弹出有同名文件的提示,基本都是一些ProjectSettings文件夹下的东西,我选择了跳过
好了,切换到Unity下时发现正在加载导入的资源,过了一会儿之后导入成功
可以在Unity窗口下的Assets下也看到LuaFramework和Plugins也导入了
在这里插入图片描述
嗯,接下来试一试能不能用

C#和Lua交互

参考了ulua的文档——链接
在我在自己的Assets文件夹下的Scripts文件夹下新建了一个CSharpLuaTest.cs文件,将这个C#脚本绑在摄像头上,开始编写C#代码。
看文档说是用LuaScriptMgr更加高效,因为使用了去反射机制(不懂2333),但是我自己写的时候发现没有这个LuaScriptMgr类了,于是用的是LuaState类创建lua虚拟机。

Dostring

通过Dostring函数可以直接执行string中的lua代码

LuaState luaState = new LuaState(); // 创建lua虚拟机,要放到Start()函数里
luaState.DoString("print('HelloWorld!')"); // 执行String形式的Lua代码

报错:
报错这是因为没有加上Start()函数,这在文档中没有写到…自己掉坑里了然后试着加上Start()函数就对了

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

public class CSharpLuaTest : MonoBehaviour {

    private LuaState luaState;

	// Use this for initialization
	void Start ()
    {
        luaState = new LuaState(); // 创建lua虚拟机,要放到Start()函数里
        luaState.Start(); // 这一句要加上
        luaState.DoString("print('HelloWorld!')"); // 执行String形式的Lua代码
    }
}

OK,运行!HelloWorld!
HelloWorld

Dofile

Dofile是执行文件中的lua代码
想到可以通过先读取lua文件转化成string,然后是用上面的Dostring函数一样可以执行

TextAsset scriptFile;
// scriptFile读取文件
luaState.DoString(scriptFile.text);

不过直接有个Dofile函数,何乐而不为呢
在Assets文件夹下建立了一个StreamingAssets文件夹,在StreamingAssets文件夹里面创建了一个Test.lua文件:
Test.luaCSharpLuaTest.cs的Start()函数中增加:

扫描二维码关注公众号,回复: 5924815 查看本文章
luaState.DoFile(Application.streamingAssetsPath + "/Test.lua");// 执行文件中的Lua代码

OK!运行!
HelloWorld

C#访问Lua变量

CSharpLuaTest.cs的Start()函数改动如下:

// Use this for initialization
void Start ()
{
	luaState = new LuaState(); // 创建lua虚拟机,要放到Start()函数里
	luaState.Start(); // 这一句要加上
	
	luaState["luaVar"] = 10; // 设置luaVar的值为10
	luaState.DoFile(Application.streamingAssetsPath + "/Test.lua"); // 执行文件中的Lua代码
	Debug.Log("Read from lua:" + luaState["luaVar"].ToString()); // 读取Lua中的luaVar值
}

Test.lua改动如下:

print('luaVar is '..luaVar)
luaVar = 15

上述代码的执行过程为:C#中先设置luaVar的变量值为10,然后执行lua代码;lua代码中打印luaVar的值并将其改变至15,;C#读取改变后的luaVar变量值并且打印
OK!运行!
C#访问Lua变量然后试试读取lua中的Table:
C#代码:

void Start ()
{
    luaState = new LuaState(); // 创建lua虚拟机,要放到Start()函数里
    luaState.Start(); // 这一句要加上
    
    luaState["luaVar"] = 10; // 设置luaVar的值为10
    luaState.DoFile(Application.streamingAssetsPath + "/Test.lua"); // 执行文件中的Lua代码
    Debug.Log("Read from lua:" + luaState["luaVar"].ToString()); // 读取Lua中的luaVar值

    LuaTable luaTable = (LuaTable)luaState["luaTable"];
    LuaDictTable<string, string> luaDictTable =  luaTable.ToDictTable<string, string>();
    foreach(LuaDictEntry<string, string> lde in luaDictTable )
    {
        Debug.Log(lde.Key + ": " + lde.Value);
    }
}

lua代码:

print('luaVar is '..luaVar)
luaVar = 15

luaTable = {['name'] = 'wawayu', ['age'] = 3}

OK!运行!
读取TableC#实际是引用了lua中的table,因此可以通过C#中直接修改或者增加table的变量,例如:

luaDictTable["level"] = "1";

对应的lua中的table也会增加这个level变量

C#调用Lua function

C#代码如下:

// Use this for initialization
void Start ()
{
    luaState = new LuaState(); // 创建lua虚拟机,要放到Start()函数里
    luaState.Start(); // 这一句要加上
    
    luaState["luaVar"] = 10; // 设置luaVar的值为10
    luaState.DoFile(Application.streamingAssetsPath + "/Test.lua"); // 执行文件中的Lua代码

    LuaFunction f = luaState.GetFunction("luaFun");
    object[] obj = f.LazyCall("I called a lua function");
    print(obj[0]);
    print(obj[1]);
}

lua代码如下:

function luaFun(str)
	print(str)
	return 'I am called', 'How are you?'
end

在lua代码中定义了一个函数,传入一个参数,返回两个string
在C#代码中可以使用object[]数组获取lua函数的返回值,这里lua返回来两个string,所以object[]数组有两个值——(注意是C#自带的object数组,而不是UnityEngine的Object数组)
使用f.Call()也可以调用函数,但是Call的返回值为void,所以这里使用的是f.LazyCall()
OK!运行!
call lua function

lua协程

注意是lua的协程,而不是Unity的协程!两者有区别!
个人理解:
Unity的协程更像是一个轻量级的线程,辅助调用它的主线程做事
lua的协程更像是一种中断保存,它有运行态、挂起态、停止态,通过yield和resume在挂起和运行之间切换
C#代码:

void Start ()
{
    luaState = new LuaState(); // 创建lua虚拟机,要放到Start()函数里
    luaState.Start(); // 这一句要加上
    
    luaState["luaVar"] = 10; // 设置luaVar的值为10
    luaState.DoFile(Application.streamingAssetsPath + "/Test.lua"); // 执行文件中的Lua代码

    LuaFunction f = luaState.GetFunction("luaFun");
    f.Call();
    f.Call();
}

lua代码:

function corFun()
	print("coroutine start")
	for i = 1, 10 do
		print(i)
		coroutine.yield()
	end
	print("coroutine end")
end

local co = coroutine.create(corFun)

function luaFun()
	coroutine.resume(co)
end

上述代码的意思是:luaFun中启动协程,在被启动的协程函数corFun每次循环都会挂起自身;在C#中两次调用了luaFun
结果如下:
协程

C#向lua传递数组作为参数

C#代码:

void Start ()
{
    luaState = new LuaState(); // 创建lua虚拟机,要放到Start()函数里
    luaState.Start(); // 这一句要加上
    
    luaState["luaVar"] = 10; // 设置luaVar的值为10
    luaState.DoFile(Application.streamingAssetsPath + "/Test.lua"); // 执行文件中的Lua代码

    LuaFunction f = luaState.GetFunction("luaFun");
    string[] objs = { "aaa", "bbb", "ccc" };
    object[] r = f.LazyCall((object)objs);//注意这里需要将参数转成object,否则数组会被当成多参数传递
    for (int i = 0; i < r.Length; i++)
    {
        Debug.Log(r[i].ToString());
    }
}

lua代码

function luaFun(array)
	for i = 0, array.Length - 1 do
		print(array[i])
	end
	return 1, '123', true
end

需要注意的地方有两点:

  1. C#中Call的时候需要(object)objs,将数组参数转化为object类型,不然会被当做多参数传递
  2. 在lua中,读取数组内容时不能使用ipairs和pairs操作,因为传递过去的类型为userdata而不是lua中的table

结果如下:
数组参数传递

小结

大致搞清楚了ulua的基本用法,文档中还有一些像C#类的重载、C#的委托机制等,这些我在自学C#的时候就有些懵,就先跳过了
等搞清楚了再写篇博客
加油!与君共勉!

猜你喜欢

转载自blog.csdn.net/ACwawayu/article/details/89344702
今日推荐