• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

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

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

说明

一直都想实现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

需要注意的地方有两点:

  1. C#中Call的时候需要(object)objs,将数组参数转化为object类型,不然会被当做多参数传递
  2. 在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还要高,哈哈哈哈误打误撞反而选了个更好的


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
TinyXML与lua的绑定及使用发布时间:2022-07-22
下一篇:
lua的变量查找及全局变量发布时间:2022-07-22
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap