信息化管理系统 | 数字孪生 · 智慧园区 · 数字大屏 | App · 微信 · 小程序 | 元宇宙 · 区块链 · 3D展厅 | 虚拟仿真系统 | 新零售电商

游戏制作:三年全职 Rust 游戏开发,真要放弃 Rust 吗?(6)

热重载对于迭代速度的重要性比人们想象的要高"

 

作者强烈推荐每个游戏开发者观看 Tomorrow Corporation Tech Demo[11] 这个视频,以了解热重载、可逆调试和游戏开发的整体工具。

Tomorrow Corporation 的团队所做的事情:

  • 构建了自己的编程语言、代码编辑器、游戏引擎、调试器和游戏。
  • 在整个堆栈中构建了热重载的支持。
  • 可逆的时间旅行调试,具有可以在游戏状态之间切换的时间线。
  • 其他...只需观看视频 :) 保证你不会后悔

作者也知道在现有平台(如.NET)或本地语言(如 C++或 Rust)中构建类似的功能几乎是不可能的,但他也不同意因为它很难并且不会百分之百地工作,我们就不应该追求这些东西的观点。

Unity 选择 C# 语言不是没有原因的,因为 C# 支持热重载。在 Unity 中,现在还有一个专门为 Unity 定制的自定义实现 hotreload.net作者表示,这是他回到 Unity 开发游戏而放弃 Rust 的首要原因。也是他们选择 Unity 而不是 Godot 或 UE5 的原因。(目前 Godot 不支持.NET 热重载,UE 仍然只有 blueprints 和 C++。)

Rust 语言层面确实不支持“热重载”,但是生态库中有提供一些方案,比如 hot-lib-reloader-rs[12] ,是基于 libloading 的一个方案,可以和 Bevy 配合使用。另外还可以通过使用 lua 语言来实现热加载,比如 `yazi`[13]

但是,这使用起来肯定不如语言级支持更加方便,再加上 Rust ABI 不稳定,所以动态库的这种方案也不是很通用。

作者坦言,他尝试过 hot-lib-reloader ,但是发现它远非完美,即使对于仅重新加载函数的非常简单的用例也是如此。他曾经遇到过许多随机的问题,最终放弃了,因为它带来的麻烦比节省的麻烦还多。即使这个 crate 没有任何问题,它也无法解决随机调整事物的问题,因为它仍然需要计划和远见,这会减少潜在的创造性使用。

 

“抽象不是一个选择”

 

在很多编程语言中,尤其是那些动态类型的语言,抽象层级可以根据程序员的偏好来调整。程序员可以选择在高层次抽象或者尽可能接近底层操作,这取决于他们对控制的需求或对性能的关注。然而,Rust 语言的设计强制要求开发者进行一定程度的安全抽象,特别是在处理可变状态和共享状态时

作者提到的 UI 编程场景中(使用 egui-rs),他本希望直接将状态传递到需要的地方,以简化代码和减少开发复杂度。然而,由于 Rust 的借用规则,这种直接的方法会引起编译器错误,因为它违反了 Rust 的并发借用规则(不能同时有可变和不可变引用)。因此,作者被迫对状态管理进行额外的抽象,例如通过克隆状态来避开借用规则的限制。

代码语言:javascript

复制

if let Some(character) = &self.selected_duck {
 character_select_duck_detail(.., character, self);
} else {
 character_select_no_duck(...);
}

作者展示的这部分代码,处理了根据当前选择的 duck 的状态(是否有选中的 duck),动态决定渲染哪个详细信息面板。这里使用了 Rust 的 if let 结构来进行条件判断和解构。

作者在尝试将 selfself 的字段作为参数传递给函数时遇到了问题。因为 Rust 不允许同时可变和不可变地借用同一个对象(self),这迫使开发者必须更细粒度地管理状态的所有权,或使用如克隆这样的方法来绕过这些限制。这种对生命周期和所有权的严格要求,实际上强制了一种对代码组织和数据访问的抽象

在 Rust 中,这种抽象不仅仅是选择,更多时候是编程模型和语言安全特性的必然要求。这保证了程序的安全和可靠,但同时也增加了编程的复杂度,特别是在需要频繁访问和修改共享状态的 UI 编程中。

这种必须的抽象化不仅可能增加代码的复杂性,还可能降低开发效率,因为它迫使开发者花费时间处理语言规则,而非直接解决业务问题。作者通过使用小丑表情来表达对这种情况的幽默感和一些讽刺意味,强调虽然 Rust 在很多方面表现出色,但在某些日常开发任务中,它的严格性可能会导致效率低下。

怎么说呢,世界上没有完美的事物,这或许就是 Rust 语言内存安全保证的黑暗面 吧。

 

“Rust 的 GUI 状况很糟糕”

 

作者认为游戏 UI 是一些需要高度风格化和视觉化的东西,构建游戏 UI 最困难的部分是定制 UI 的外观和体验。

作者用了 egui 构建了大部分的 UI,但感觉它在很多方面都不是最佳选择。

egui 的作者创业在做多模态可视化框架 rerun

Rust GUI 生态确实很糟糕。

 

“响应式 UI 不是制作高度可视化、独特和交互式游戏 UI 的答案”

 

Rust 中有许多 GUI 库,采用了许多不同的方法。有些是现有 GUI 库的绑定,有些是即时模式,有些是响应式的,甚至还有保留模式。有些尝试使用 flexbox,而其他一些在基本层面上并不真正处理布局。

在作者看来,游戏 GUI 并不太关心数据更新得有多快,也不关心是否具有响应式重新渲染、数据绑定或最花哨的声明式布局描述方式。

相反,我想要一个非常漂亮的 GUI,有许多自定义精灵、动画、矢量形状、粒子、特效、闪光等等。当点击按钮时,我希望它摇晃;当悬停在文本上时,我希望它动画化;我希望能够使用自定义着色器并用噪声纹理扭曲它。当选择一个角色框时,我希望粒子飞来飞去。

在 Rust 生态系统中还没有一个单一的解决方案将其目标定为“擅长制作游戏 GUI”。

 

“孤儿规则应该是可选的”

 

孤儿规则,简单来说,就是「你要实现的 trait 和 类型必须有一个在本地定义」。目前在 Rust 中是强制的。

孤儿规则的存在是合理的,因为可以避免各种 trait 实现的冲突。

但作者认为,在有些场景,应该允许「关闭孤儿规则」。游戏引擎和框架就是一个很好的例子,使用 MacroquadComfy 这样的库的人并不需要在他们的代码库中遵守孤立规则。对于"框架型"的库来说,能够在不分叉的情况下扩展现有功能,并为最终用户提供更统一的体验将非常有益。

 

"编译时间有所改善,但是处理过程宏时没有改善"

 

Rust 的编译时间整体情况有所改善,至少在 Linux 上是如此。在 Windows 上增量构建仍然明显较慢,以至于迫使作者转移到了 Linux(3-5 倍的差异)。但是在购买了一台新的高端台式机之后,构建他们的 10k 行代码库只需要几秒钟。

作为一个很好的例子, comfy-ldtk 的存在仅仅是为了包装一个单独的文件,并确保 serde 的单态化发生在一个单独的 crate 中。这可能看起来是一个琐碎的细节,这导致了增量时间从 Linux 上的 2 秒增加到了 10 秒。对于 1600 行结构定义来说,这是一个相当巨大的差异。

作者觉得有些人意识到,如果他们的编译时间是 0.5 秒而不是 30 秒,他们的游戏会有多么精致。GUI 之类的东西本质上是需要不断调整的,除了 godot-rust 的用户之外,其他人都将不得不多次重启游戏以使界面看起来好看。如果你对此表达异议,那么就请给作者提供一个使用+30 秒增量构建时间构建的非常精致的 GUI 的例子。

 

对于 GUI 开发这确实是个问题,但不代表所有情况都是个问题

 

“Rust 游戏开发生态圈存在炒作”

 

作者认为 Bevy 在市场营销方面比较出色。

就在几天前,Brackeys 发布了他们关于回归游戏开发并使用 Godot 进行开发的视频。当我观看这个视频并开始听到所有那些令人惊叹的开源游戏引擎时,我已经有了一种感觉。在大约 5:20 的时候,视频中展示了一张游戏引擎市场地图的图片,我只能说看到了三个 Rust 游戏引擎,而且特别是哪三个:Bevy、Arete 和 Ambient。现在我想要明确一点,这篇博文并不是对任何特定项目的攻击,我理解这些项目并不对其他人在他们的视频中做的事情负责。但与此同时,这已经成为 Rust 世界中的一个主题,甚至可以说是一个模因,我觉得应该谈谈这个问题。

Rust 生态系统通常的运作方式是,无论该项目的可用性如何,只要能够做出最多的承诺、展示最好的网站/自述文件、拥有最炫目的 GIF 图像,并且最重要的是能够吸引正确的抽象价值观,就会受到广泛赞扬。然后还有其他一些项目通常处于低调状态,因为它们不够吸引人,也没有承诺无法实现的功能,而只是试图以一种有效的方式完成某个任务,这些项目几乎从不被提及,或者当提及时被视为次等选择。

为此,作者举了三个例子:

  • Macroquad,它是一个非常实用的 2D 游戏库,可以在几乎所有平台上运行,具有非常简单的 API,编译速度非常快,几乎没有依赖,并且是由一个人构建的。还有一个配套的库 miniquad ,在 Windows/Linux/MacOS/Android/iOS 和 WASM 上提供了一个图形抽象层。然而,Macroquad 在 Rust 生态系统中犯了一个最严重的错误,那就是使用全局状态,甚至可能是 unsafe 的。每当有人提到它时,这个问题将永远被提及,因为它不符合 Rust 的最终价值,即 100%的安全性和正确性。
  • Fyrox,它是一个具有实际完整 3D 场景编辑器、动画系统和似乎满足制作游戏所需的一切的 3D 游戏引擎。这个项目也是由一个人完成的,这个人还在该引擎中制作一个完整的 3D 游戏。尽管它拥有一个完整的编辑器,而 Bevy 多年来一直在反复承诺这一点(但没有提供),它不如 Bevy 懂得营销,没有那么多 star 。
  • godot-rust ,它 Godot 引擎的 Rust 绑定。这个库最严重的问题是它不是一个纯 Rust 的解决方案,而只是对一个“肮脏”的 C++ 引擎的绑定。有点夸张,但那些从外部看 Rust 的人可能会惊讶地发现这有时是多么接近现实。Rust 是纯净的,Rust 是正确的,Rust 是安全的。C++ 是糟糕、陈旧、丑陋、不安全和复杂的。这就是为什么在 Rust 游戏开发中,我们不使用 SDL,我们有 winit ,我们不使用 OpenGL,我们有 wgpu ,我们不使用 Box2D 或 PhysX,我们有 rapier ,我们有 kira 用于游戏音频,我们不使用 Dear ImGUI,我们有 egui ,最重要的是,我们肯定不能使用一个用 C++ 编写的现有游戏引擎。那将违反每个使用 rustup default nightly 以获得更快编译时间的人在许可证中达成的神圣螃蟹法典(同样禁止我们正式使用由 Rust 基金会认可的标志(商标))。

如果有人真的想在 Rust 中制作一个真正的游戏,尤其是 3D 游戏,我的第一推荐是使用 Godot 和 godot-rust ,因为至少它们有机会提供他们所需的所有功能,因为他们可以依靠一个真正的引擎来帮助他们交付。我们花了一年时间使用 Godot 3 和 gdnative 以及 godot-rust 来构建 BITGUN,虽然这个过程在很多方面都很痛苦,但这并不是绑定的错,而是因为我们试图以各种可能和动态的方式混合大量的 GDScript 和 Rust。这是我们的第一个也是最大的 Rust 项目,也是我们走上 Rust 之路的原因,最终我要说,我们之后用 Rust 制作的每个游戏都不再像一个游戏,仅仅是因为我们花了很多时间来解决与 Rust 语言、生态系统的某些部分或者某些设计决策相关的无关技术问题,这些问题由于语言的严格性而难以解决。我不会说 GDScript 和 Rust 的互操作很容易,它绝对不是。但至少有一个“只需做事情并继续前进”的选项提供了 Godot。我觉得大多数只尝试纯代码解决方案的人并不重视这一点,尤其是在 Rust 中,语言可能会以许多不方便的方式阻碍创造力。就 Bevy 而言,我确实相信它被展示为“主要”的 Rust 游戏引擎在很大程度上是合理的,如果有什么原因,那是因为项目的规模和参与人数。他们成功建立了一个非常庞大的社区,虽然我可能不同意他们的承诺和领导层的一些选择,但我不能否认 Bevy 很受欢迎。

不得不说,作者讲的这一点,确实也是事实。作为 Rust 社区的深受 Rust 语言文化影响的我,也非常认同作者说到的这一点:想要纯 Rust 实现的一切。可能我也是一个纯粹主义者。

但我觉得纯粹主义者没什么毛病,这个世界上每个人都有自己的喜好。我们可以选择自己喜欢用的,但没有去强迫别人也去用自己喜欢的,就没什么问题。我们选择自己喜欢用的库和框架,不是原罪吧

Bevy 作为一个开源商业项目,构建自己的社区,培养自己的生态,营销有毛病吗?作者可能是认为 Bevy 过度营销了,一直承诺的东西没有实现。但这里面真的没有夹杂个人情绪?

Rust / bevy 真多过度营销了吗?你怎么看呢?

 

 

如有侵权请通知删除,谢谢!

本文转自;三年全职 Rust 游戏开发,真要放弃 Rust 吗?-腾讯云开发者社区-腾讯云 (tencent.com)