探索游戏脚本语言:从 Warcraft III 的 JASS 到现代游戏开发的便捷性提升

昵称 4个月前 45浏览 0评论

一、引言

自从我还是个沉浸在《魔兽争霸III》的改装魔法中的孩子以来,我就一直对游戏的脚本语言有一种特殊的感情。

当年,使用暴雪的JASS语言来开发《魔兽争霸3》游戏关卡游戏开发,虽然从今天来看,JASS极其初级,主要表现为静态打字+没有GC功能,但它代表了在行业标准形成之前的时代,对一种游戏开发语言的大胆尝试。

为什么要使用脚本语言来开发游戏?

游戏脚本语言的引入主要是为了提高开发和测试的易用性。如果你直接使用像 C++ 这样的低级语言,你将不得不花费大量时间等待一个复杂的工具链来编译和打包你更改的每一行代码。通过使用脚本语言,可以热加载和执行实现游戏玩法的程序,这大大提高了游戏的开发效率。

随着时间的流逝游戏开发,像 Lua 和 JavaScript 这样的动态类型脚本语言已经成为游戏开发中的常规语言。

然而,随着编程语言的发展,我们有机会重新定义游戏脚本语言的新标准——既复古又创新,那就是 Rust + WASM 的结合。

2. Rust + WASM + Dora SSR:重新定义游戏脚本开发

通过将 Rust 和 WASM 相结合,我们可以直接在 Android 或 iOS 等设备上热更新和测试游戏,而不会牺牲性能,而无需依赖传统的应用程序开发工具链。

此外,借助 Dora SSR 开源游戏引擎的 Web IDE 接口,用 Rust 编写的游戏代码可以编译一次,并在多个游戏设备上测试运行。

为什么选择 Rust?

Rust 提供了无与伦比的内存安全保证,无需垃圾收集器 (GC) 的参与,这使其成为游戏开发的理想选择,尤其是在性能敏感的场景中。结合 WASM,Rust 能够在保持跨平台一致性和安全性的同时提供高性能的执行效率。

快速入门指南

在开始开发之前,我们需要安装 Dora SSR 游戏引擎。该引擎支持多种平台,包括 Windows、Linux、macOS、iOS 和 Android。

有关安装步骤和要求的更多信息,请参阅官方快速入门指南:Dora SSR Quick Start

步骤 1:创建新项目

Dora的SSR引擎的二进制程序启动后,在浏览器中打开Dora SSR Web IDE,在左侧的游戏资源树上右键,选择“新建”,并创建一个名为“Hello”的新文件夹。

第 2 步:编写游戏代码

然后在命令行中创建一个新的 Rust 项目:

rustup target add wasm32-wasi
cargo new hello-dora --name init
cd hello-dora
cargo add dora_ssr

在 src/main.rs 中编写代码:

use dora_ssr::*;

fn main () {
let mut sprite = match Sprite::with_file("Image/logo.png") {
Some(sprite) => sprite,
None => return,
};
let mut sprite_clone = sprite.clone();
sprite.schedule(once(move |mut co| async move {
for i in (1..=3).rev() {
p!("{}", i);
sleep!(co, 1.0);
}
p!("Hello World");
sprite_clone.perform_def(ActionDef::sequence(&vec![
ActionDef::scale(0.1, 1.0, 0.5, EaseType::Linear),
ActionDef::scale(0.5, 0.5, 1.0, EaseType::OutBack),
]));
}));
}

Build 生成 WASM 文件:

cargo build --release --target wasm32-wasi

第 3 步:上传并运行游戏

在Dora SSR Web IDE中,右键点击新创建的文件夹“Hello”,选择“Upload”,上传编译好的WASM文件init.wasm。

或者使用帮助脚本 upload.py 通过以下命令将 WASM 文件上传到 Rust 项目文件夹中,其中 IP 参数是 Dora SSR 启动后会显示的 Web IDE 地址,后一个参数是要上传的目录的相对路径:

python3 upload.py "192.168.3.1" "Hello"

第 4 步:发布游戏

在编辑器左侧的游戏资源树中,右键点击刚刚创建的项目文件夹,然后选择“下载”。

等待浏览器弹出下载打包项目文件的提示。

3. 如何实现它

在 Dora SSR 中实现 Rust 语言开发支持和 WASM 运行时嵌入的过程,是一次新的技术探索和尝试,包括三个关键步骤:

1. 接口定义语言(IDL)设计。

要在用 C++ 编写的游戏引擎上嵌入 WASM 运行时并支持 Rust 语言,首先需要设计一种接口定义语言 (IDL),以促进不同编程语言之间的通信和数据交换。

以下是 Dora SSR 设计的一个 WASM IDL 示例,可以看出它基于源语言 C++ 的程序接口,并添加了一些标签,用于转换为 Rust 接口所需的信息,如 object、readonly、optional 等。

做跨语言接口映射的难点之一是 C++ 的接口设计是面向对象的,但 Rust 没有提供完整的面向对象设计能力,因此一些面向对象的接口需要在 Rust 上编写额外的代码进行功能模拟。

object class EntityGroup @ Group
{
readonly common int count;
optional readonly common Entity* first;
optional Entity* find(functionEntity* e)> func) const;
static EntityGroup* create(VecStr components)
;
};

2. 生成胶水代码的程序

第二步是编写一个程序,该程序从 IDL 生成胶水代码,该代码相互调用 C++、WASM 和 Rust。

为了实现这一点,我们选择使用由 Dora SSR 项目创建的 Yuescript 语言。Yuescript 是一种基于 Lua 的动态编程语言,它结合了 Lua 语言生态系统中的 lpeg 语法解析库,用于处理 IDL 解析和胶水代码生成。

使用 Yuescript 的好处是它继承了 Lua 的灵活性和轻量级,同时提供了更丰富的语法和功能,使其适合处理复杂的代码生成任务。

以下是用 PEG 语法编写的 IDL 解析器代码的摘录。

Param = P {
"Param"
Param: V"Func" * White * Name / mark"callback" + Type * White * Name / mark"variable"
Func: Ct P"function<" * White * Type * White * Ct P"(" * White * (V"Param" * (White * P"," * White * V"Param")^0 * White)^-1 * P")" * White * P">"
}

Method = Docs * Ct(White * MethodLabel) * White * Type * White * (C(P"operator==") + Name) * White * (P"@" * White * Name + Cc false) * White * Ct(P"(" * White * (Param * (White * P"," * White * Param)^0 * White)^-1 * P")") * White * C(P"const")^-1 * White * P";" / mark"method"

3. 嵌入 WASM 运行时和代码集成

最后一步是将 WASM 运行时和生成的 C++ 胶水代码嵌入到游戏引擎中,完成代码集成。对于 WASM 运行时,我们选择使用 WASM3,这是一款高性能、轻量级的 WebAssembly 解释器,支持多种 CPU 架构,简化了编译链的复杂性,并提高了跨平台兼容性。通过这种方式,Dora SSR 能够在各种架构的硬件设备上支持用 Rust 开发的游戏,大大提高了游戏项目的可访问性和灵活性。

作为集成的一部分,我们为 Rust 开发人员发布了一个 crate 包,其中包含所有必要的接口和工具,以便开发人员将来可以轻松开发和重新分发基于 Dora SSR 游戏引擎的用 Rust 编写的其他游戏模块。

四、性能对比

Dora SSR 游戏引擎还提供对 Lua 脚本语言的支持。我目前使用的是 Lua 5.5 VM,它与 WASM3 相同,不生成 JIT 实时机器代码,而只是在 VM 中解释和执行脚本代码。因此,我们可以对这两种类似的脚本方案进行一些性能比较。

在对比之前,我们可以大致判断,无论在 Lua 语言中执行 GC 需要多长时间,由于 Lua 语言本身的动态性质,在 C++ 中映射到 Lua 的程序接口往往需要对接口传入的参数类型进行实时检查, 而对 Lua 对象的成员属性的访问也需要在运行时通过一个带有哈希结构的表来找到,这是静态类型的 Rust 语言 + WASM 虚拟机不需要支付开销。或者只使用开销较小的方案。

以下是一些基本性能测试的示例,具体选择的是 C++ 端不做太多计算处理的接口,用于比较跨语言调用参数的性能差异。

let mut root = Node::new();
let node = Node::new();

let start = App::get_elapsed_time();
for _ in 0..10000 {
root.set_transform_target(&node);
}
p!("object passing time: {} ms", (App::get_elapsed_time() - start) * 1000.0);

let start = App::get_elapsed_time();
for _ in 0..10000 {
root.set_x(0.0);
}
p!("number passing time: {} ms", (App::get_elapsed_time() - start) * 1000.0);

let start = App::get_elapsed_time();
for _ in 0..10000 {
root.set_tag("Tag name");
}
p!("string passing time: {} ms", (App::get_elapsed_time() - start) * 1000.0);

local root = Node()
local node = Node()

local start = App.elapsedTime
for i = 1, 10000 do
root.transformTarget = node
end
print("object passing time: " .. tostring((App.elapsedTime - start) * 1000) .. " ms")

start = App.elapsedTime
for i = 1, 10000 do
root.x = 0
end
print("number passing time: " .. tostring((App.elapsedTime - start) * 1000) .. " ms")

start = App.elapsedTime
for i = 1, 10000 do
root.tag = "Tag name"
end
print("string passing time: " .. tostring((App.elapsedTime - start) * 1000) .. " ms")

运行结果

Rust + WASM:
object passing time: 0.6279945373535156 ms
number passing time: 0.5879402160644531 ms
string passing time: 3.543853759765625 ms

Lua:
object passing time: 6.7338943481445 ms
number passing time: 2.687931060791 ms
string passing time: 4.2259693145752 ms

可以看出,除了字符串类型的接口参数调用外,Dora SSR 中实现的其他类型接口的 Lua 跨语言调用性能几乎比 WASM 跨语言调用慢一个数量级。

字符串类型接口的推论是,性能成本主要在字符串对象的复制上,跨语言调用的成本比内存复制的成本要小得多,因此结果的差异并不大。

第五,用户体验

随着 Rust 在游戏开发中的引入,我亲身体验到了与传统不同的生产力提升,尤其是在使用 ChatGPT 等大型语言模型生成代码时。与传统的 C 或 C++ 相比,Rust 的严格编译器为游戏开发提供了更强大、更安全的编程环境。

例如,在使用大型语言模型辅助编码时,在生成 C 或 C++ 甚至许多动态类型语言时,虽然生成的代码在许多情况下都可以编译,但在运行时仍然隐藏着许多难以察觉的 bug 和缺陷。这些问题可能包括内存泄漏、指针或引用误用等,这些问题在游戏开发中很常见且难以调试。

然而,在 Rust 中,由于 Rust 的所有权和借用机制,以及它在类型安全和内存安全方面的设计优势,其中许多问题可以在编译时被有效地捕获和修复。

通过在 Dora SSR 游戏引擎中引入对 Rust 的支持,我发现编写游戏脚本不仅更安全,而且效率更高。这使得游戏开发不再是故障排除过程,而是更专注于创建和实现假想的游戏。

Rust 的这些优势,结合 WASM 的跨平台能力,极大地拓展了我们的游戏开发能力和可能性。

六、结语

选择Dora SSR + Rust作为游戏开发工具,不仅是对技术前沿的追求,更是对游戏开发过程的全新探索。我们邀请所有热爱游戏开发的人加入我们的社区,一起探索这个激动人心的技术之旅。

我们的Q组来了,欢迎来玩:512620381

关于作者

金力:金融行业大数据工程师,《Dora SSR》和《Yuescript》开源软件的作者。

项目介绍

Dora SSR(Doro Curiosity Engine)是一款用于在多种设备上快速开发2D游戏的游戏引擎。它内置了易于使用的开发工具链,支持在手机、开源手持设备和其他设备上直接进行游戏开发

项目仓库

热门文章

⬆️ 武汉远创将火热注册

点击关闭
  • 客服QQ:

    744926664

    -------------------

分享:

支付宝

微信