Unity3d 中使用Lua之UniLua

本文介绍如何在Unity3D中利用Lua语言及其UniLua库进行动态游戏更新与功能扩展。通过加载Lua脚本、调用Lua函数和从Lua调用C#函数,实现高效的游戏逻辑定制与增强。同时,文章提供了一个简单的编码库enclib用于处理UTF-8与UTF-16编码转换,确保跨平台兼容性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Unity3d 中使用Lua之UniLua

方便动态更新游戏用。

开源项目地址:https://github.com/xebecnan/UniLua

最新支持到Lua5.2,C#版的Lua

基础用法:

大部分的使用是可以参考标准的 Lua 官方文档和 Lua 教程的。 Lua 本身的语法是一样的。C API 和 C# API 之间有个对应关系。例如 lua_pushnumber() 这个 C API 对应到 UniLua 里就是 lua.PushNumber()

所有标准 lua 中 lua.h 和 lauxlib.h 里定义的接口,都对应 LuaAPI.cs 里定义的 ILuaAPI 和 LuaAuxLib.cs 里定义的 ILuaAuxLib 接口。

从 C# 调用 Lua

最朴素的从 C# 调用 lua 的一个全局函数的写法:

Lua.GetGlobal( "foo" ); // 加载 lua 中定义的一个名叫 foo 的全局函数到堆栈
Debug.Assert( Lua.IsFunction(-1) ); // 确保加载成功了, 此时栈顶是函数 foo
Lua.PushString( "test" ); // 将第一个参数(字符串 "test")入栈
Lua.PushInteger( 42 ); //将第二个参数(整数 42)入栈
Lua.Call(2, 0); // 调用函数 foo, 指明有2个参数,没有返回值
// 上面的代码相当于 lua 里一个这样的调用 foo("test", 42)

稍微复杂一点的例子可以参考实例程序里的一些简单写法:参考这个文件 Assets/Behaviour/LuaScriptController.cs:

// 创建 Lua 虚拟机
var Lua = LuaAPI.NewState();
// 加载基本库
Lua.L_OpenLibs();
// 加载 Lua 脚本文件
var LuaScriptFile = "framework/main.lua";
var status = Lua.L_DoFile( LuaScriptFile );
// 捕获错误
if( status != ThreadStatus.LUA_OK )
{
    throw new Exception( Lua.ToString(-1) );
}
// 确保 framework/main.lua 执行结果是一个 Lua Table
if( ! Lua.IsTable(-1) )
{
  throw new Exception(
        "framework main's return value is not a table" );
}
// 从 framework/main.lua 返回的 table 中读取 awake 字段指向的函数
// 并保存到 AwakeRef 中 (可以将 AwakeRef 视为这个函数的句柄)
var AwakeRef = StoreMethod( "awake" );
// 不再需要 framework/main.lua 返回的 table 了,将其从栈上弹出
Lua.Pop(1);
//----------------------------------------------------
// 在需要的时候可以这样调用 AwakeRef 指向的 lua 函数
CallMethod( AwakeRef );
//----------------------------------------------------
// StoreMethod 和 CallMethod 的实现
private int StoreMethod( string name )
{
    Lua.GetField( -1, name );
    if( !Lua.IsFunction( -1 ) )
    {
        throw new Exception( string.Format(
            "method {0} not found!", name ) );
    }
    return Lua.L_Ref( LuaDef.LUA_REGISTRYINDEX );
}
private void CallMethod( int funcRef )
{
    Lua.RawGetI( LuaDef.LUA_REGISTRYINDEX, funcRef );
    var status = Lua.PCall( 0, 0, 0 );
    if( status != ThreadStatus.LUA_OK )
    {
        Debug.LogError( Lua.ToString(-1) );
    }
}

从 Lua 调用 C# 函数 ( 使用 C# 来扩展 Lua 功能 )

目前的示例程序是使用 FFI 库来实现的 从 Lua 调用 C# 函数。 FFI 因为用到了反射机制来调用 C# 函数,性能会比较低。应该尽量避免使用,如果没有找到更好的办法,准备之后把这个FFI实现废弃掉。其实直接用 C# 实现一个库的形式,来让 lua 调用这种传统的做法效率会比较高,也是推荐采用的方式。而且也并不会麻烦太多。

比如我现在要实现一个叫 libfoo 的库, 里面提供两个方法: add(a, b) 和 sub(a, b)

库的实现

using UniLua;
public static class LibFoo
{
    public const string LIB_NAME = "libfoo.cs"; // 库的名称, 可以是任意字符串
    public static int OpenLib(ILuaState lua) // 库的初始化函数
    {
        var define = new NameFuncPair[]
        {
            new NameFuncPair("add", Add),
            new NameFuncPair("sub", Sub),
        };
        lua.L_NewLib(define);
        return 1;
    }
    public static int Add(ILuaState lua)
    {
        var a = lua.L_CheckNumber( 1 ); // 第一个参数
        var b = lua.L_CheckNumber( 2 ); // 第二个参数
        var c = a + b; // 执行加法操作
        lua.PushNumber( c ); // 将返回值入栈
        return 1; // 有一个返回值
    }
    public static int Sub(ILuaState lua)
    {
        var a = lua.L_CheckNumber( 1 ); // 第一个参数
        var b = lua.L_CheckNumber( 2 ); // 第二个参数
        var c = a - b; // 执行减法操作
        lua.PushNumber( c ); // 将返回值入栈
        return 1; // 有一个返回值
    }
}

库的初始化

// 创建 Lua 虚拟机
var Lua = LuaAPI.NewState();
// 加载基本库
Lua.L_OpenLibs();
Lua.L_RequireF( LibFoo.LIB_NAME  // 库的名字
              , LibFoo.OpenLib   // 库的初始化函数
              , false            // 不默认放到全局命名空间 (在需要的地方用require获取)
              );

库的使用 (在 lua 代码中)

// 获取库
local libfoo = require "libfoo.cs"
// 调用库的方法
print(libfoo.add(42, 1))
print(libfoo.sub(42, 22))

UTF-8 support

C# 采用 UTF-16 作为字符串的内部编码,而 Lua 本身没有实现比较完善的编码支持。为了处理这个问题,我实现了一个简单的编码库 enc lib。使用方法如下:

-- Assuming your source code is in utf-8.
-- convert from utf-8:
local utf8_str = '测试字符串'
local print_safe_str = enc.decode(utf8_str, 'utf8')
print(print_safe_str)
-- convert to utf-8:
local original_str = enc.encode(print_safe_str, 'utf8')
assert(utf8_str == original_str)
更多资料:http://dong2008hong.blog.163.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值