高性能混合编程:用脚本语言配合C++使用之Lua

Lua

Lua 与其他语言最大的不同点在于它的设计目标:不追求“大而全”,而是“小而美”。
Lua实现业务逻辑简单,可以配合在C++里使用,也可让C++配合Lua使用,C++可以实现底层和服务器一些高性能方面的应用,Lua可以实现一些复杂的业务逻辑,将两者配合使用,具体应用在不同的场景里,加快开发效率。

标准的 Lua(PUC-Rio Lua)使用解释器运行,速度虽然很快,但和 C/C++ 比起来还是有差距的。

使用LuaJIT代替标准Lua,效率还是很高的。
可以用 LuaJIT :https://luajit.org/ ,用了 JIT(Just in time)技术,能够把 Lua 代码即时编译成机器码,速度几乎可以媲美原生 C/C++ 代码。
缺点:更新较慢

建议:改用非官方的一个分支项目:OpenResty-LuaJIT
这个由 OpenResty 负责维护,非常活跃,修复了很多小错误。

git clone git@github.com:openresty/luajit2.git
make && make install

LuaBridge 是一个纯头文件的库,只要下载下来,把头文件拷贝到包含路径,就能够直接用:

git clone git@github.com:vinniefalco/LuaBridge.git

在Lua里调用C++

LuaJIT内置了一个 ffi 库(Foreign Function Interface),能够在 Lua 脚本里直接声明接口函数、直接调用,不需要任何的注册动作,更加简单方便。而且这种做法还越过了 Lua 传统的栈操作,速度也更快。

注意:只能识别纯 C 接口,不认识 C++
一定要将接口转换成C接口,内部可以用C++。

包含头文件的使用extern “C”


extern "C" {                // 使用纯C语言的对外接口
int num = 10; 
int my_add(int a, int b); 
}

int my_add(int a, int b)    // 一个简单的函数,供Lua调用
{
    return a + b;
}

然后就可以用 g++ 把它编译成动态库


g++ lua_shared.cpp -std=c++11 -shared -fPIC -o liblua_shared.so

然后Lua脚本
首先要用 ffi.cdef 声明要调用的接口,
再用 ffi.load 加载动态库,
这样就会把动态库所有的接口都引进 Lua,
然后就能随便使用了:


local ffi = require "ffi"            -- 加载ffi库
local ffi_load = ffi.load            -- 函数别名
local ffi_cdef = ffi.cdef

ffi_cdef[[                           // 声明C接口
int num;
int my_add(int a, int b);
]]

local shared = ffi_load("./liblua_shared.so")    -- 加载动态库 

print(shared.num)                                -- 调用C接口
local x = shared.my_add(1, 2)                    -- 调用C接口

在 ffi 的帮助下,让 Lua 调用 C 接口几乎是零工作量,但这并不能完全发挥出 Lua 的优势。

实际上,很少在Lua里调用C++,一般都是嵌入到别的语言宿主里使用。被宿主调用,然后再“回调”底层接口,利用它的“胶水语言”特性去粘合业务逻辑。

C++里调用Lua的使用方法:

方法一:
首先要调用函数 luaL_newstate(),创建出一个 Lua 虚拟机,所有的 Lua 功能都要在它上面执行。
然后,用完之后使用函数lua_close() 关闭。
可以使用RAII的方法写一个类来自动管理。
LuaBridge 没有对此封装,自己动手实现,借用课中例子:
用了智能指针 shared_ptr,在一个 lambda 表达式里创建虚拟机,顺便再打开 Lua 基本库:
1、创建Lua虚拟机


auto make_luavm = []()              // lambda表达式创建虚拟机
{
    std::shared_ptr<lua_State> vm(  // 智能指针
        luaL_newstate(), lua_close  // 创建虚拟机对象,设置删除函数
        );
    luaL_openlibs(vm.get());        // 打开Lua基本库

    return vm; 
};
#define L vm.get()                  // 获取原始指针,宏定义方便使用

在 LuaBridge 里,一切 Lua 数据都被封装成了 LuaRef 类,完全屏蔽了 Lua 底层那难以理解的栈操作。
它可以隐式或者显式地转换成对应的数字、字符串等基本类型,如果是表,就可以用“[]”访问成员,如果是函数,也可以直接传参调用,非常直观易懂。

注意:使用 LuaBridge 访问 Lua 数据时,只能用函数 getGlobal() 看到全局变量。想在 C++ 里调用 Lua 功能,就一定不能加“local”修饰。

2、调用上述函数创建了一个 Lua 虚拟机,然后获取了 Lua 内置的 package 模块,输出里面的默认搜索路径 path 和 cpath:


auto vm = make_luavm();                  // 创建Lua虚拟机
auto package = getGlobal(L, "package");  // 获取内置的package模块

string path  = package["path"];          // 默认的lua脚本搜索路径
string cpath = package["cpath"];         // 默认的动态库搜索路径

或者,直接调用luaL_dostring() 和 luaL_dofile() 直接执行 Lua 代码片段或者外部的脚本文件。
注意:luaL_dofile() 每次调用都会从磁盘载入文件,所以效率较低。
如果频繁调用,存入内存。存成一个字符串,再用 luaL_dostring() 运行:


luaL_dostring(L, "print('hello lua')");  // 执行Lua代码片段
luaL_dofile(L, "./embedded.lua");        // 执行外部的脚本文件

方法二:
提前在脚本里写好一些函数,加载后在 C++ 里逐个调用,这种方式比执行整个脚本更灵活。
具体做法:
先用 luaL_dostring() 或者 luaL_dofile() 加载脚本,然后调用 getGlobal() 从全局表里获得封装的 LuaRef 对象,就可以像普通函数一样执行了。


string chunk = R"(                    -- Lua代码片段
    function say(s)                   -- Lua函数1
        print(s)
    end
    function add(a, b)                -- Lua函数2
        return a + b
    end
)";

luaL_dostring(L, chunk.c_str());      // 执行Lua代码片段

auto f1 = getGlobal(L, "say");        // 获得Lua函数
f1("say something");                  // 执行Lua函数

auto f2 = getGlobal(L, "add");        // 获得Lua函数
auto v = f2(10, 20);                  // 执行Lua函数

只要掌握了上面的这些基本用法,并合理地划分出 C++ 与 Lua 的职责边界,就可以搭建出“LuaJIT + LuaBridge + C++”的高性能应用,运行效率与开发效率兼得。比如说用 C++ 写底层的框架、引擎,暴露出各种调用接口作为“业务零件”,再用灵活的 Lua 脚本去组合这些“零件”,写上层的业务逻辑。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 游戏脚本通常使用脚本语言来编写,例如 Lua、JavaScript 和 Python。这些脚本语言通常用于编写游戏中的非常高层的代码,例如实现游戏规则、游戏流程、任务和对话等。另外,还有一些游戏引擎,比如 Unity 和 Unreal Engine,提供了自己的脚本语言或脚本编辑器,用于编写游戏逻辑。 此外,游戏脚本还可能使用其他编程语言来编写,比如 C++ 和 C#。这些语言通常用于编写游戏的底层代码,例如渲染引擎、物理引擎和其他底层系统。 总的来说,游戏脚本可以使用多种不同的编程语言来编写,具体使用哪种语言取决于游戏开发者的需求和偏好。 ### 回答2: 游戏脚本可以使用多种编程语言来编写。最常见的编程语言是脚本语言,如Python、Lua和Ruby等。这些语言具有简单易学的语法和高度灵活的特性,非常适合用于编写游戏脚本。 Python是一种流行的脚本语言,被广泛用于游戏开发。它具有简洁的语法和强大的库支持,可以方便地实现各种游戏逻辑和功能。 Lua是另一种常见的脚本语言,被广泛应用于游戏引擎中。它具有轻量级和快速执行的特点,非常适用于嵌入式系统和游戏脚本开发。 Ruby是一种简洁优雅的脚本语言,也可以用于游戏脚本开发。它具有强大的元编程能力,可以在游戏中实现高级的脚本功能。 除了脚本语言之外,游戏脚本还可以使用其他编程语言,如C++和C#等。这些语言通常用于游戏的核心逻辑和性能敏感的部分,可以提供更高的执行效率和更灵活的控制。 总体而言,游戏脚本的选择取决于开发团队的偏好和游戏需求。不同的编程语言都有各自的优势和适用范围,开发者可以根据实际情况选择最合适的语言来编写游戏脚本。 ### 回答3: 游戏脚本使用编程语言取决于游戏的引擎和开发工具。 一些常见的游戏引擎,如Unity和Unreal Engine,通常使用C#或C++作为其脚本编程语言。使用这些引擎,开发人员可以编写游戏逻辑和行为脚本。C#是一种面向对象的编程语言,易于学习和使用,并且具有强大的功能,如垃圾回收和丰富的类库。 另外,脚本编程语言还可以包括Python和Lua等语言。Python是一种简单易用的脚本语言,它具有易读易写的语法和丰富的第三方库,适合编写游戏中的一些小脚本。而Lua是一种轻量级脚本语言,具有快速的执行速度和简洁的语法,常用于游戏引擎的脚本编写。 对于更特定的游戏类型,可能会使用其他特定领域的编程语言。例如,网页游戏开发可以使用JavaScript语言,移动游戏开发可以使用Java或Objective-C/Swift语言。这些编程语言都有相应的开发工具和库,使开发人员能够更好地创建游戏逻辑和功能。 总之,选择使用哪种编程语言主要取决于游戏的需求和开发团队的经验。不同的语言有不同的优势和适用场景,开发人员需要根据具体情况选择最适合的编程语言来编写游戏脚本。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值