Unity 初次使用ulua,熟悉C#和Lua交互
说明
一直都想实现Unity的热更新,但因为种种原因搁置了。前段时间学习了一下Lua的基本语法,发现这门语言还真是简单易学。于是实现Unity热更新的想法又开始蠢蠢欲动了…
看了一些大佬们的博客,发现C#和Lua之间交互的框架还挺多,思来想去最后决定使用ulua框架。
选择ulua框架的原因是它全平台支持,而且通过LuaJit进行加速,效率上不会太低,而且使用简单,导入package就可以编写代码了。不过ulua的作者只提供了ulua的插件包,并没有提供整套插件源码,但对于我这种新手来说也没什么影响…反正源码也看不懂,好用就行哈哈哈哈哈哈
我主要参考的是这篇博客,整理得非常详细——[整理]Unity3D游戏开发之Lua
下载
首先,当然要先下载ulua包,我实在这个网站上下载的——链接
这个网站里面的网盘链接已经失效了,我是通过GitHub下载的
附上GitHub地址——链接
不知道是不是网络的问题,download总是失败,所以最后我选择拷贝HTTPS链接然后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!
Dofile
Dofile是执行文件中的lua代码
想到可以通过先读取lua文件转化成string,然后是用上面的Dostring函数一样可以执行
TextAsset scriptFile;
// scriptFile读取文件
luaState.DoString(scriptFile.text);
不过直接有个Dofile函数,何乐而不为呢
在Assets文件夹下建立了一个StreamingAssets文件夹,在StreamingAssets文件夹里面创建了一个Test.lua文件: CSharpLuaTest.cs的Start()函数中增加:
luaState.DoFile(Application.streamingAssetsPath + "/Test.lua");// 执行文件中的Lua代码
OK!运行!
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!运行! 然后试试读取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!运行! C#实际是引用了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!运行!
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
需要注意的地方有两点:
- C#中Call的时候需要(object)objs,将数组参数转化为object类型,不然会被当做多参数传递
- 在lua中,读取数组内容时不能使用ipairs和pairs操作,因为传递过去的类型为userdata而不是lua中的table
结果如下:
小结
大致搞清楚了ulua的基本用法,文档中还有一些像C#类的重载、C#的委托机制等,这些我在自学C#的时候就有些懵,就先跳过了
等搞清楚了再写篇博客
加油!与君共勉!
今天研究了一下,发现自己上面用的其实是基于tolua#的LuaFramework_UGUI
看了官方的说明,大概各种区别如下:
ulua:支持反射+warp方式;
cstolua:基于ulua,更快;
SimpleFramework_NGUI/UGUI:基于ulua的框架,是ulua如何使用的演示,分别支持NGUI和UGUI两种Unity的UI版本;
tolua#:不支持反射(暂时)+warp方式,基于ulua,是ulua第三代超级热更新方案,效率比ulua还要高;
LuaFramework_NGUI/UGUI:基于tolua的框架,新的引擎新的框架!
因为自己也是初学者,找下载资源的时候也是懵懵的,下了个以为是ulua的东西。。。但就官方说法而言,tolua#效率比ulua还要高,哈哈哈哈误打误撞反而选了个更好的
|
请发表评论