Rust并不是来取代C++的,尽管它们在语法上是相似的。Rust提供内存安全作为其核心功能,这就是为什么那些喜欢Rust的人真的喜欢它。Rust的用途也很广泛:它可以用于网络开发、游戏开发、文件系统、操作系统,以及更多。
C++是一种面向对象的编程语言,可用于游戏开发、操作系统、网络浏览器、云/分布式系统等。它在游戏开发中特别受欢迎,因为它的性能高,抽象性强,而且有很多库和工具。
在本指南中,我们将在游戏开发的背景下比较Rust和C++。我们将介绍Rust和C的异同,以及在游戏开发中使用每种编程语言的利弊。我们还将向你介绍一些用Rust和C++进行游戏开发的工具。
以下是我们将讨论的内容。
- 为什么C++在游戏开发中很受欢迎
- 面向C++开发者的游戏引擎
- 用于游戏开发的C++工具
- 游戏开发中的Rust。我们的游戏了吗?
- 为什么使用Rust进行游戏开发?
- 面向对象的编程与面向数据的编程
- 面向Rust开发者的游戏引擎
- 用于游戏开发的Rust工具
- Rust的不足之处
- C++ vs. Rust。哪一个最适合你的游戏开发项目?
为什么C++在游戏开发中很受欢迎
在游戏开发行业,C++已经存在了相当长的一段时间。很多游戏开发者选择用C系列的其他语言甚至是汇编语言来补充它。
C++把它的帽子挂在高性能和强抽象性上。开发者选择C++也是因为它的继承功能和其他面向对象的模型所提供的功能。任何在游戏行业工作了一段时间的人都可以证明用C++构建游戏的工具的广泛存在。对于那些需要在最后期限前完成工作或刚进入游戏行业的开发者来说,由于有大量的工具和资源可用,C++始终是首选。
面向C++开发者的游戏引擎
由于C++在游戏开发社区中的地位由来已久,所以用C++构建的游戏引擎种类繁多。让我们来比较一些最受欢迎的C++游戏开发者的工具。
Blender
Blender是一个自由和开源软件(FOSS)的3D制作套件。它完全用C++构建,并提供对OpenAL 3D声音和Python脚本的支持。鉴于它是跨平台的,Blender支持大多数主要的操作系统。游戏开发并不是Blender的全部,你还可以制作短片和其他电影元素。
统一
Unity是一个跨平台的游戏引擎,使你能够创建2D、3D和虚拟现实游戏。虽然它主要是作为一个MAC OS X专用的游戏引擎,但Unity后来被许多电影、工程和建筑应用所采用。
熊猫3D
一些游戏引擎要求你使用外部库来进行碰撞检测、I/O、音频等。Panda3D在一个包中提供了所有这些和更多的东西。这个用C++编写的游戏引擎允许你用Python编写游戏,尽管用C++编写游戏有一个变通方法。
戈多
Godot是一个开源的、跨平台的游戏引擎,里面有很多工具,可以让你专注于你的游戏开发。这个游戏引擎是用C++构建的,由于它支持的灵活性,对于用C++进行游戏开发来说相当受欢迎。
用于游戏开发的C++工具
与Rust不同,大多数C++的游戏引擎都包含了你开发游戏所需的所有工具。
在用C++构建游戏时,仔细考虑哪个引擎最适合你的项目是很重要的。了解面向对象编程的一般概念也很重要,因为你将与面向对象的模型打交道。
游戏开发中的Rust。我们的游戏开始了吗?
为了了解Rust提供了什么,以及为什么它是一种对游戏开发有用的语言,让我们在历史的列车上坐一坐,了解为什么它首先被创建。
Rust最初是Mozilla的一个员工Graydon Hoare的一个副业,他解决了C++中的大部分漏洞。随着时间的推移,Mozilla用户对C++的内存泄漏和其他漏洞感到失望,而C++是其网络浏览器Firefox的核心语言。
这就是为什么Graydon Hoare建议使用Rust,这是他从2006年开始研究的一种语言。直到2010年,Mozilla才开始支持Rust,因为它在内存安全方面表现出很大的进步。
为什么使用Rust进行游戏开发?
为什么有人愿意使用一种新的语言进行游戏开发,而不是使用已经存在很久的C系列语言呢?这是因为像Rust这样的内存安全语言消除了用户在使用你的产品时将面临的许多错误。内存安全语言不会允许有内存泄漏的代码运行。为了实现内存安全,Rust使用了面向数据的模型。它将游戏元素视为数据,而不是像面向对象编程中的对象。
面向对象与面向数据的编程
游戏开发中的面向对象编程存在一些问题–尤其是面向对象编程的核心特征之一,封装。封装帮助开发者隐藏数据,以维持一个安全的环境。然而,在游戏开发中,这个功能是一把脚枪,因为它违背了它的创建目的。
例如,你需要遵循继承原则才能访问数据,因为由于封装,你不能将其公开。因此,对于游戏中添加的每一个新功能,你可能需要从另一个字段中访问数据,而这个字段可能是被封装的,这样他们就可以继承这个功能。
为了了解游戏开发中与OOP相关的封装/继承的弊端,让我们看看Catherine West在RustConf 2018的闭幕式主题演讲中的这个快速例子。
typedef uint32_t EntityId;
// Declare World to pass it to Entity
struct World;
struct InputState { ... };
struct RenderState { ... };
// Pure virtual interface!
class Entity {
public:
virtual Vec2F position() const = 0;
void update(World* world) = 0;
void input(InputState const& input_state) = 0;
void render(RenderState& render_state) = 0;
private:
};
class Player : Entity {
public:
Vec2F position() const override;
void input(InputState const& input_state) override;
void update(World* world) override;
void render(RenderState& render_state) override;
private:
Physics m_physics;
HumanoidState m_humanoid;
...
};
class Monster : Entity {
public:
Vec2F position() const override;
void input(InputState const& input_state) override;
void update(World* world) override;
void render(RenderState& render_state) override;
private:
Physics m_physics;
...
};
class NPC : Entity {
public:
Vec2F position() const override;
void input(InputState const& input_state) override;
void update(World* world) override;
void render(RenderState& render_state) override;
private:
Physics m_physics;
HumanoidState m_humanoid;
...
};
struct WorldTile { ... };
struct World {
List<EntityId> player_ids;
HashMap<EntityId, shared_ptr<Entity>> entities;
MultiArray2D<WorldTile> tiles;
...
};
复制代码
随着你的项目的发展,你的应用程序中的许多子、父和祖先关系将变得难以处理,并可能在你的项目中产生一个漏洞。例如,如果我们的游戏在未来的版本中得到一个新的功能,我们就需要应用继承。
比方说,我们需要一个怪物来追踪健康状况不佳的玩家。要做到这一点,我们必须为玩家的健康状况创建一个公共访问器,因为它是私有的。
class Monster : Entity {
public:
Vec2F position() const override;
void input(InputState const& input_state) override;
void update(World* world) override;
void render(RenderState& render_state) override;
DamageRegion const& damage_region() const;
private:
...
};
复制代码
如果我们想为那些私有的状态添加更多的功能,那么我们需要创建更多的访问器。这样做的话,我们就会不断地在我们的应用程序上挖洞,直到它变得不安全,无法被管理。
因为Rust采取的是面向数据的方法,所以游戏元素被当作数据处理。Rust在游戏开发中使用了实体组件系统(ECS)模式,实体由附加在它身上的不同组件组成,组件,由大块的数据(游戏开发的数据)组成,而系统则管理应用程序的逻辑。例如,如果我们想在Rust中复制C++中的同一个例子,我们将ECS方法与实体和组件作为结构。
type EntityIndex = usize;
struct Physics {
position: Vector2<f32>,
velocity: Vector2<f32>,
mass: f32,
}
struct HumanoidAnimationState { ... }
struct HumanoidItem { ... }
struct HumanoidState {
animation_state: HumanoidAnimationState,
left_hand_item: HumanoidItem,
right_hand_item: HumanoidItem,
aim_position: Vector2<f32>,
}
struct Player {
physics: Physics,
humanoid: HumanoidState,
health: f32,
focused_entity: EntityIndex,
food_level: f32,
admin: bool,
...
}
enum MonsterAnimationState { ... }
struct DamageRegion { ... }
struct Monster {
physics: Physics,
animation_state: MonsterAnimationState,
health: f32,
current_target: EntityIndex,
damage_region: DamageRegion,
...
}
struct NpcBehavior { ... }
struct Npc {
physics: Physics,
humanoid: HumanoidState,
health: f32,
behavior: NpcBehavior,
...
}
enum Entity {
Player(Player),
Monster(Monster),
Npc(Npc),
}
struct Assets { ... }
struct GameState {
assets: Assets,
entities: Vec<Option<Entity>>,
players: Vec<EntityIndex>,
...
}
fn main() {
let mut game_state = initial_game_state();
loop {
let input_state = capture_input_state();
player_control_system(&mut game_state, &input_state);
npc_behavior_system(&mut game_state);
monster_behavior_system(&mut game_state);
physics_system(&mut game_state);
// ... lots more systems
render_system(&mut game);
audio_system(&mut game);
wait_vsync();
}
}
复制代码
新的功能可以很容易地被添加到结构中。为了避免重复,你可以使用impl
关键字。使用这种方法,可以很容易地检索或传递数据给一个特征,而不需要继承,因为组件可以在需要的时候被调用进来。
Rust开发者的游戏引擎
尽管Rust在游戏开发领域是一种相对较新的语言,但已经有很多用Rust构建的游戏引擎可供选择。让我们看看一些顶级的Rust游戏引擎箱,并简要地探讨如何在游戏开发中使用它们。
Amethyst
Amethyst是面向数据的,快速的,易于配置的。它有大规模的并行架构,使用ECS模型,并允许使用RON文件进行快速原型开发。
Amethyst使那些刚接触游戏开发的开发者能够快速上手。游戏引擎提供的例子可以帮助你轻松地熟悉。要运行任何一个例子,在你选择的命令行界面中执行下面的命令。
cargo run --example name_of_example
复制代码
Caper
Caper支持其他系统,包括音频、渲染、输入和碰撞检测。它不是一个跨平台的游戏引擎,它只支持Linux操作系统。像Amethyst一样,Caper提供了一些例子来帮助你掌握游戏引擎的方向。你可以通过在你的命令行界面上运行下面的命令来测试这些例子。
cargo run --example transforms
复制代码
Chariot
Chariot是对微软发布的 “帝国时代 “游戏的重新实现,它使用了Genie引擎。Chariot是一个开源的游戏引擎,可以被移植到任何想要的平台上。这个游戏引擎的目标是制作类似上述标题的游戏。
控制台
如果你想要一个能提供处理用户输入的工具的引擎,Console是你最好的选择。有了Console引擎,如果你不想要终端、鼠标或键盘处理,你甚至可以轻松地创建独立的屏幕。
Oxygengine
Oxygengine是一个用Rust和web-sys编写的网页游戏引擎。它是一个基于Specscrate的HTML5和WebAssembly游戏引擎,用于其ECS框架。
其他值得注意的用Rust编写的游戏引擎包括 bevy
, coffee
, corange
, doryen
, dotrix
, muoxi
, [rusty_engine](https://crates.io/crates/rusty_engine)
, turbine
,以及更多。
用于游戏开发的Rust工具
正如我们之前提到的,工具化在游戏开发中起着重要作用。在本节中,就像我们对C++所做的那样,我们将高屋建瓴地看看一些用于游戏开发的Rust工具。
二维渲染
渲染是游戏创作的一个重要部分,因为给你的产品的用户提供了一个具有二维的、逼真的图像的吸引人的用户界面。一些用于Rust游戏开发的顶级2D渲染工具包括。
三维渲染
虽然2D渲染提供了二维的逼真图像,但你可能也能猜到,3D渲染使你的游戏环境通过三维图像显得更加逼真。下面是一些对Rust游戏开发者最有用的3D渲染工具。
人工智能(AI
人工智能库使你能够使用算法在你的游戏中实现预测性行为。例如,有一些人工智能库带有预先构建的国际象棋算法,你可以用来在Rust中创建这样的游戏。用于游戏开发的Rust人工智能库的突出例子包括。
动画库
大多数游戏都需要运动。Rust中的动画库使你能够操纵图像,使其表现得像在移动。由于大多数Rust库都是由社区成员建立的,而且Rust是一种相对较新的语言,因此在撰写本文时,Pareen是唯一广泛使用的Rust游戏开发的动画库。
Pareen允许你创建以时间为参数的动画,而不需要在周围传递时间变量。这对于在多个游戏状态之间创建平滑的过渡非常有用。
音频包装器
在游戏开发中,声音和动作一样重要。例如,一个动作游戏,如果没有逼真的轰鸣声、撞击声和其他与残骸和破坏有关的声音,就会感到不完整和无聊。
当你想在你的Rust游戏中实现音频时,下面的Rust音频封装器列表是一个很好的开始。
输入库
对于使用垫子和其他输入设备的游戏,你需要一个板块来处理输入设备中的控制器。Rust有两个输入库。
网络工具
当你和朋友一起玩的时候,游戏会变得更加有趣。Rust生态系统包括一系列网络工具,以帮助促进开发人员之间的合作,并促进Rust游戏中的多人游戏功能,包括。
碰撞检测库
在某些类型的游戏中,当用户与某些东西发生碰撞时就会失败或获得积分。碰撞检测库就像它的名字一样:检测你游戏中的碰撞情况。
对Rust游戏开发者来说,有用的碰撞检测库包括。
UI库
用户界面是玩家对你的游戏的第一印象和判断,甚至在参与和体验游戏之前。在游戏开发中,第一印象就是一切,一个糟糕的用户界面往往会让玩家在开始与你的游戏互动之前就失去兴趣。
一些用于Rust游戏开发的UI库是。
VR引擎
2021年,游戏开发社区的一些部分正趋向于虚拟现实,创造出令人惊叹的逼真视觉景观,以前所未有的方式包围和沉浸在玩家中。
下面是Rust所提供的一些最好的VR引擎。
Rust的不足之处
在用Rust构建游戏时,必须了解大多数Rust工具和引擎仍处于开发阶段。而且,要重申的是,Rust的游戏开发方法是面向数据的。因此,如果你是来自于像C++这样的面向对象的背景,在开始用Rust进行游戏开发之前,你应该花一些时间熟悉面向数据的模型。
关于面向对象编程在游戏开发中带来的挑战的进一步阅读,请查看Catherine West在RustConf 2018的闭幕主题演讲。
C++ vs. Rust。哪一个最适合你的游戏开发项目?
以我的愚见,不存在完美的编程语言,甚至不存在固有的编程语言。任何工作的最佳语言、框架、库或工具都取决于你使用它的舒适程度以及你项目的独特要求和目标。
工具的可用性和支持也是游戏开发者的首要考虑因素。因此,如果你正在构建一个以内存安全为优先的游戏,Rust可能是你的最佳选择。在Discord和其他地方都有社区支持和交流渠道。你可以通过访问Are We Game Yet来了解最新情况,并跟踪Rust在游戏开发方面的生产准备情况。
另一方面,对于不需要内存安全的游戏开发项目,C++是一个很好的选择。C++的生态系统包括更广泛的、久经考验的工具,这些工具已经存在多年,在游戏开发者社区中受到信任。如果你对面向对象的编程比使用Rust等面向数据的语言更得心应手,那么C++是你项目的一个特别有力的选择。
总结
在本指南中,我们探讨了用C++和Rust编程语言进行游戏开发的基础知识。我们比较了使用Rust和C++构建游戏的开发者经验;列出了实现动画、声音、碰撞检测、多人游戏功能等方面最有用和最广泛采用的工具;并定义了一些简单的参数,以确定哪种语言最适合你的游戏开发项目。
The postRust vs. C++ for game developmentappeared first onLogRocket Blog.