第一章:你好,Rust
关于本教程
本教程是免费且开源的,所有代码使用 MIT 许可证——因此您可以随意使用它。我希望您会喜欢这个教程,并制作出很棒的游戏!
如果你喜欢这个并且希望我继续写作,请考虑支持我的 Patreon。
本教程主要关于学习制作 roguelike 游戏(以及扩展到其他游戏),但它也应该能帮助你熟悉 Rust 和 RLTK——我们将使用的Roguelike 工具包来提供输入/输出。即使你不想使用 Rust,我也希望你能从结构、想法和一般游戏开发建议中受益。
为什么选择 Rust?
Rust 首次出现在 2010 年,但直到最近才达到“稳定”状态——也就是说,你现在编写的代码在语言变化时不太可能停止工作。开发仍在进行中,语言的全新部分(如异步系统)仍在出现/稳定中。本教程将避开开发的最前沿——它应该是稳定的。
Rust 被设计为一种“更好的系统语言”——即像 C++
一样低级,但机会更少会让你自己陷入困境,专注于避免使 C++ 开发变得困难的许多“陷阱”,并且对内存和线程安全有极大的关注:它的设计使得编写一个破坏其内存或遭受竞态条件的程序变得非常困难(不是不可能,但你必须努力尝试!)。它正在迅速获得关注,从 Mozilla 到 Microsoft 都表现出兴趣——并且有越来越多的工具用它编写。
Rust 也被设计为比 C++ 拥有更好的生态系统。Cargo
提供了一个完整的包管理器(在 C++ 领域中,vcpkg
、conan
等也提供了类似的功能,但 Cargo 集成得更好),一个完整的构建系统(类似于 cmake
、make
、meson
等,但标准化了)。它不像 C 或 C++ 那样在许多平台上运行,但这个列表在不断增长。
我尝试了 Rust(在朋友的强烈推荐下),发现虽然它并没有取代 C++在我日常工具箱中的位置——但有时它确实帮助我快速完成项目。它的语法需要一些时间适应,但它确实能很好地融入现有基础设施。
学习 Rust
如果你使用过其他编程语言,那么有很多帮助可用!
- Rust 编程语言书籍 提供了对语言的优秀自顶向下介绍。
- 通过示例学习 Rust 更接近我喜欢的学习方式(我已经有很多语言的经验),提供了大多数你可能遇到的主题的常见用法示例。
- 24 天学习 Rust 提供了一个稍微以 Web 为重点的 24 天课程来学习 Rust。
- 面向 JavaScript 开发者的 Rust 所有权模型 如果你来自 JS 或其他非常高级的语言,应该会有所帮助。
如果你发现你需要的东西不在那里,很可能有人已经写了一个crate
(在其他语言中称为“包”,但 cargo 处理的是 crate...)来帮助你。一旦你有了一个工作环境,你可以输入cargo search <my term>
来查找有用的 crate。你也可以前往crates.io查看 Cargo 中提供的所有 crate 的完整列表,包括文档和示例。
如果你是完全的编程新手,那么有一个坏消息:Rust 是一种相对年轻的语言,所以目前还没有很多“从零开始学习 Rust 编程”的资料——还没有。你可能会发现从一种更高层次的语言开始,然后再“向下”(更接近底层)学习 Rust 会更容易。然而,如果你决定尝试,上面的教程/指南应该能帮你入门。
获取 Rust
在大多数平台上,rustup 足以让你获得一个可用的 Rust 工具链。在 Windows 上,这是一个简单的下载——完成后你会得到一个可用的 Rust 环境。在类 Unix 系统(如 Linux 和 OS X)上,它提供了一些命令行指令来安装环境。
安装完成后,通过在命令行中输入 cargo --version
来验证它是否正常工作。你应该会看到类似 cargo 1.36.0 (c4fcfb725 2019-05-15)
的内容(版本会随时间变化)。
熟悉开发环境
你想为你的开发工作创建一个目录/文件夹(我个人使用users/herbert/dev/rust
- 但这只是个人选择。它真的可以是你喜欢的任何地方!)。你还需要一个文本编辑器。我是Visual Studio Code的粉丝,但你可以使用任何你习惯的编辑器。如果你使用 Visual Studio Code,我推荐以下扩展:
更好的 TOML
: 使阅读 toml 文件变得愉快;Rust 经常使用它们C/C++
: 使用 C++调试系统调试 Rust 代码Rust (rls)
: 不是最快的,但提供全面的语法高亮和实时错误检查。
一旦你选择了你的环境,打开一个编辑器并导航到你的新文件夹(在 VS Code 中,文件 -> 打开文件夹
并选择该文件夹)。
创建一个项目
现在你已经在你选择的文件夹中,你想在那里打开一个终端/控制台窗口。在 VS Code 中,这是终端 -> 新建终端
。否则,像往常一样打开命令行并cd
到你的文件夹。
Rust 有一个内置的包管理器叫做 cargo
。Cargo 可以为你创建项目模板!所以,要创建你的新项目,输入 cargo init hellorust
。片刻之后,你的项目中会出现一个名为 hellorust
的新文件夹。它将包含以下文件和目录:
src\main.rs
Cargo.toml
.gitignore
这些是:
.gitignore
在你使用 git 时非常方便 - 它可以防止你不小心将不需要的文件放入 git 仓库。如果你不使用 git,可以忽略它。src\main.rs
是一个简单的 Rust "hello world" 程序源码。Cargo.toml
定义了你的项目以及如何构建它。
快速 Rust 介绍 - Hello World 的剖析
自动生成的 main.rs
文件如下所示:
fn main() { println!("Hello, world!"); }
如果你使用过其他编程语言,这看起来应该有些熟悉——但语法/关键字可能不同。Rust 最初是 ML 和 C 的混合体,旨在创建一种灵活的“系统”语言(意味着:你可以为你的 CPU 编写裸机代码,而不需要像 Java 或 C# 那样的虚拟机)。在此过程中,它继承了这两种语言的许多语法。我发现使用它的第一周语法看起来糟糕,之后就变得相当自然。就像人类语言一样,你的大脑需要一段时间才能适应语法和布局。
那么这一切意味着什么?
fn
是 Rust 的关键字,用于表示 函数。在 JavaScript 或 Java 中,这会写成function main()
。在 C 中,它会写成void main()
(尽管在 C 中main
应该返回一个int
)。在 C# 中,它会是static void Main(...)
。main
是函数的 名称。在这种情况下,名称是一个特殊情况:操作系统需要知道在将程序加载到内存时首先运行哪个函数 - 而 Rust 会额外工作以将main
标记为第一个函数。通常,如果您希望程序执行任何操作,您 需要 一个main
函数,除非您正在制作一个 库(供其他程序使用的函数集合)。()
是函数的 参数 或 形参。在这种情况下,没有参数 - 所以我们只使用空的括号。{
表示 块 的开始。在这种情况下,块是函数的 主体。在{
和}
之间的所有内容都是函数的内容:执行的指令。块还表示 作用域 - 因此您在函数内部声明的任何内容都仅限于该函数。 换句话说,如果你在一个名为cheese
的函数中创建一个变量 - 它将不会在名为mouse
的函数中可见(反之亦然)。有一些方法可以解决这个问题,我们将在构建游戏的过程中介绍它们。println!
是一个 宏。你可以通过名称后面的!
来识别 Rust 宏。你可以在这里学习所有关于宏的知识;现在,你只需要知道它们是 特殊 函数,在编译期间会被解析成 其他代码。打印到屏幕上可能会非常复杂 - 你可能想说不仅仅是 "hello world" - 而println!
宏涵盖了 很多 格式化情况。(如果你熟悉 C++,它相当于std::fmt
。大多数语言都有自己的字符串格式化系统,因为程序员往往需要输出大量文本!)- 最后的
}
关闭了在4
中开始的块。
继续输入 cargo run
。经过一些编译后,如果一切正常,你将在终端上看到 "Hello World"。
有用的 cargo
命令
Cargo 是一个非常棒的工具!你可以在《学习 Rust 书籍》中了解一些关于它的内容 从《学习 Rust 书籍》,如果你感兴趣,可以从《Cargo 书籍》中了解 所有 关于它的内容 《Cargo 书籍》。
在 Rust 工作中,你会经常与 cargo
打交道。如果你用 cargo init
初始化你的程序,你的程序就是一个 cargo crate。编译、测试、运行、更新 - Cargo 可以帮助你完成所有这些工作。它甚至默认为你设置 git
。
您可能会发现以下 cargo
功能很方便:
cargo init
创建一个新项目。这就是你用来创建 hello world 程序的方法。如果你真的不想使用git
,可以输入cargo init --vcs none (项目名称)
。cargo build
下载项目的所有依赖并编译它们,然后编译你的程序。它实际上不会运行你的程序——但这是一个快速查找编译错误的好方法。cargo update
将获取你在cargo.toml
文件中列出的 crates 的新版本(见下文)。cargo clean
可以用来删除项目的所有中间工作文件,释放大量磁盘空间。它们会在你下次运行/构建项目时自动下载和重新编译。偶尔,cargo clean
可以在某些功能不正常时提供帮助——特别是 IDE 集成。cargo verify-project
会告诉你你的 Cargo 设置是否正确。cargo install
可以通过 Cargo 安装程序。这对于安装你需要的工具很有帮助。
Cargo 还支持扩展——即插件,使其功能更加强大。以下是一些你可能会发现特别有用的扩展:
- Cargo 可以将所有源代码重新格式化为标准的 Rust 代码,就像 Rust 手册中的那样。您需要输入
rustup component add rustfmt
一次 来安装该工具。完成后,您可以随时输入cargo fmt
来格式化代码。 - 如果您想使用
mdbook
格式 - 用于 这本书!- Cargo 也可以提供帮助。只需一次,您需要运行cargo install mdbook
将工具添加到您的系统中。之后,mdbook build
将构建一个图书项目,mdbook init
将创建一个新项目,而mdbook serve
将为您提供一个本地网络服务器来查看您的工作!您可以在 他们的文档页面 上了解有关mdbook
的所有信息。 - Cargo 还可以与一个名为
Clippy
的“代码检查工具”集成。Clippy 有点挑剔(就像他的 Microsoft Office 同名一样!)。只需一次,运行rustup component add clippy
。现在,您可以随时输入cargo clippy
来查看代码中可能存在的问题建议!
创建一个新项目
让我们修改新创建的“hello world”项目,以使用RLTK——Roguelike 工具包。
设置 Cargo.toml
自动生成的 Cargo 文件将如下所示:
[package]
name = "helloworld"
version = "0.1.0"
authors = ["如果它知道的话,你的名字"]
edition = "2018"
# 在 https://doc.rust-lang.org/cargo/reference/manifest.html 查看更多键及其定义
[dependencies]
继续并确保您的名字是正确的!接下来,我们将要求 Cargo 使用 RLTK - Roguelike 工具包库。Rust 使这变得非常容易。调整 dependencies
部分,使其看起来像这样:
[dependencies]
rltk = { version = "0.8.0" }
我们告诉它包名为rltk
,并且可以在 Cargo 中找到——所以我们只需要给它一个版本。你可以运行cargo search rltk
随时查看最新版本,或者访问crate 网页。
偶尔运行 cargo update
是个好主意 - 这将更新程序使用的库。
你好,Rust - RLTK 风格!
继续并替换 src\main.rs
的内容为:
use rltk::{Rltk, GameState}; struct State {} impl GameState for State { fn tick(&mut self, ctx : &mut Rltk) { ctx.cls(); ctx.print(1, 1, "Hello Rust World"); } } fn main() -> rltk::BError { use rltk::RltkBuilder; let context = RltkBuilder::simple80x50() .with_title("Roguelike Tutorial") .build()?; let gs = State{ }; rltk::main_loop(context, gs) }
现在创建一个名为 resources
的新文件夹。RLTK 需要一些文件才能运行,我们将它们放在这里。下载 resources.zip,并将其解压到此文件夹中。注意要有 resources/backing.fs
(等等)而不是 resources/resources/backing.fs
。
保存,然后返回终端。输入 cargo run
,你将会看到一个显示 Hello Rust
的控制台窗口。
如果你是 Rust 的新手,你可能想知道Hello Rust
代码到底做了什么,以及为什么它在那里——所以我们花点时间来了解一下。
- 第一行相当于 C++的
#include
或 C#的using
。它只是告诉编译器我们将需要来自命名空间rltk
的Rltk
和GameState
类型。以前你需要在这里添加一个额外的extern crate
行,但最新版本的 Rust 现在可以为你解决这个问题。 - 使用
struct State{}
,我们正在创建一个新的结构
。结构类似于 Pascal 中的记录,或许多其他语言中的类:你可以在其中存储一堆数据,还可以将“方法”(函数)附加到它们上。在这种情况下,我们实际上不需要任何数据——我们只需要一个地方来附加代码。如果你想了解更多关于结构的信息,这是 Rust Book 中关于该主题的章节 impl GameState for State
相当冗长!我们告诉 Rust 我们的State
结构实现了GameState
特性。特性类似于其他语言中的接口或基类:它们为你设置了一个结构,你可以在自己的代码中实现,然后可以与提供它们的库进行交互——而无需该库知道你的代码的任何其他信息。 在这种情况下,GameState
是由 RLTK 提供的一个 trait。RLTK 要求你有一个 - 它使用它来在每一帧调用你的程序。你可以在 Rust 书中的这一章 了解 trait。fn tick(&mut self, ctx : &mut Rltk)
是一个 *函数 * 定义。我们在 trait 实现的作用域内,所以我们是为 trait 实现这个函数 - 因此它必须匹配 trait 所需的类型。函数是 Rust 的基本构建块,我推荐 Rust 书中的这一章。- 在这种情况下,
fn tick
意味着“创建一个名为 tick 的函数”(它被称为“tick”是因为它在每一帧渲染时“滴答”;在游戏编程中,通常将每次迭代称为 tick)。 - 它没有以
-> type
结尾,所以它相当于 C 中的void
函数 - 它在被调用后不返回任何数据。参数也可以受益于一点解释。 &mut self
表示“此函数需要访问父结构体,并且可能会更改它”(mut
是“mutable”的缩写,表示它可以更改结构体内的变量——“状态”)。你也可以在结构体中有只包含&self
的函数——这意味着我们可以查看结构体的内容,但不能更改它。如果你完全省略&self
,该函数根本无法查看结构体——但可以像调用命名空间一样调用(你经常会在调用new
函数时看到这种情况——它们为你创建结构体的新副本)。ctx: &mut Rltk
表示“传入一个名为ctx
的变量”(ctx
是“context”的缩写)。冒号表示我们正在指定它必须是哪种类型的变量。&
表示“传递一个引用”——这是一个指向现有变量副本的指针。变量不会被复制,你正在处理传入的版本;如果你进行更改,你将更改原始变量。《Rust 编程语言》对此解释得比我好。mut
再次表明这是一个“可变”引用:允许你对上下文进行更改。- 最后
Rltk
是你接收的变量的类型。在这种情况下,它是一个在RLTK
库中定义的struct
,提供了各种可以对屏幕执行的操作。
- 在这种情况下,
ctx.cls();
表示“调用变量ctx
提供的cls
函数。cls
是“清除屏幕”的常见缩写——我们告诉我们的上下文它应该清除虚拟终端。除非你特别不想这样做,否则在每一帧的开始这样做是个好主意。ctx.print(1, 1, "Hello Rust World");
要求上下文在位置 (1,1) 处打印“Hello Rust World”。- 现在我们到了
fn main()
。每个程序都有一个main
函数:它告诉操作系统从哪里开始运行程序。 -
是一个从#![allow(unused)] fn main() { use rltk::RltkBuilder; let context = RltkBuilder::simple80x50() .with_title("Roguelike Tutorial") .build()?; }
struct
内部调用函数的例子——其中该结构不接受“self”函数。在其他语言中,这被称为构造函数。我们正在调用函数simple80x50
(这是 RLTK 提供的一个构建器,用于创建一个 80 个字符宽、50 个字符高的终端。窗口标题是“Roguelike 教程”。 let gs = State{ };
是一个变量赋值的例子(参见The Rust Book)。我们正在创建一个名为gs
的新变量(代表“游戏状态”),并将其设置为我们上面定义的State
结构的副本。rltk::main_loop(context, gs)
调用rltk
命名空间中的一个名为main_loop
的函数。它需要我们之前创建的context
和GameState
- 所以我们传递这些参数。RLTK 试图简化运行 GUI/游戏应用程序的复杂性,并提供这个包装器。该函数现在接管程序的控制,并且每次程序“滴答”时(即完成一个周期并进入下一个周期)都会调用你的tick
函数(见上文)。这可以每秒发生 60 次或更多次!
希望这些解释有些作用!
使用教程
你可能想在不全部输入的情况下玩转教程代码!好消息是它已经上传到 GitHub 供你查阅。你需要安装git
(RustUp 应该已经帮你解决了这个问题)。选择你想存放教程的位置,并打开一个终端:
cd <路径到教程> git clone https://github.com/thebracket/rustrogueliketutorial .
过了一会儿,这将下载完整的教程(包括本书的源代码!)。它的布局如下(这并不完整!):
───book
├───chapter-01-hellorust
├───chapter-02-helloecs
├───chapter-03-walkmap
├───chapter-04-newmap
├───chapter-05-fov
├───resources
├───src
这里有什么?
book
文件夹包含本书的源代码。除非你想纠正我的拼写,否则可以忽略它!- 每个章节的示例代码包含在
chapter-xy-name
文件夹中;例如,chapter-01-hellorust
。 src
文件夹包含一个简单的脚本,提醒你在运行任何内容之前切换到章节文件夹。resources
包含你为此示例下载的 ZIP 文件的内容。所有章节文件夹都预配置为使用此内容。Cargo.toml
设置为包含所有教程作为“工作区条目”——它们共享依赖项,因此不会每次使用时都重新下载所有内容,占用整个驱动器。
要运行一个示例,打开您的终端并:
cd <你存放教程的位置> cd chapter-01-hellorust
cargo run
如果您使用的是 Visual Studio Code,您可以使用 文件 -> 打开文件夹 来打开您检出的整个目录。使用内置终端,您可以简单地 cd
到每个示例并 cargo run
它。
访问教程源代码
你可以在https://github.com/thebracket/rustrogueliketutorial获取所有教程的源代码。
更新教程
我经常更新这个教程——添加章节、修复问题等。你将定期想要打开教程目录,并输入 git pull
。这会告诉 git
(源代码管理器)前往 Github
仓库并查找新的内容。然后它会下载所有更改的内容,你再次拥有最新的教程。
更新您的项目
你可能会发现 rltk_rs
或其他包已经更新,而你希望获得最新版本。从你的项目文件夹中,你可以输入 cargo update
来更新所有内容。你可以输入 cargo update --dryrun
来查看它将要更新的内容,而不会更改任何内容(人们经常更新他们的 crates,所以这可能是一个很长的列表!)。
更新 Rust 本身
我不推荐在 Visual Studio Code
或其他 IDE 内部运行这个,但如果你想确保你拥有最新版本的 Rust
(以及相关工具),你可以输入 rustup self update
。这将更新 Rust 更新工具(我知道这听起来有点递归)。然后你可以输入 rustup update
并安装所有工具的最新版本。
获取帮助
有多种方式可以获得帮助:
- 如果有任何问题、改进建议或希望我添加的内容,请随时联系我(我在 Twitter 上的用户名是
@herberticus
)。 - /r/rust上的热心人士在 Rust 语言问题上非常有帮助。
- /r/roguelikedev上的热心人士在 Roguelike 问题上非常有帮助。他们的 Discord 也很活跃。 运行本章的示例与 Web Assembly,在您的浏览器中(需要 WebGL2)
版权 (C) 2019, Herbert Wolverson.
版权 (C) 2024, myedgetech.com.