Rust 与 GBA:环境搭建与像素渲染
摘要
一份教程指南,讲解如何搭建 Rust 项目以构建能在 Game Boy Advance 上运行的 ROM,涵盖项目设置和像素渲染。
<p><a href="https://lobste.rs/s/kiimof/rust_x_gba_setup_pixels">评论</a></p>
查看缓存全文
缓存时间: 2026/05/17 15:25
# Rust x GBA:环境搭建与像素绘制
来源:https://jonahnestrick.com/blog/rust-gba-tutorial-1/
本教程将指导你如何搭建一个 Rust 项目,构建一个能在 Game Boy Advance 上运行的基础 ROM。假设你已经掌握 Rust 基础知识,并熟悉软件开发流程。如果你对 Rust 还不熟悉,可以查阅 [Rust 官方书籍](https://doc.rust-lang.org/book/)(doc.rust-lang.org/book),这是一份学习这门高效编程语言的绝佳资源。
## 背景介绍(https://jonahnestrick.com/blog/rust-gba-tutorial-1/#background)
这部分我会先讲一段我的“人生故事”,然后再给你“巧克力曲奇”的配方。
点击此处直接跳转到项目搭建部分(https://jonahnestrick.com/blog/rust-gba-tutorial-1/#setup)
去年差不多这个时候,我想用 Rust 做一个底层项目。日常工作我主要用 TypeScript 写 API,但 Rust 吸引我的是它能以安全、高性能的方式建模程序执行,让开发者写出既有原生速度又具备现代语言便利性的代码。
我先买了几个 [Arduino ESP32](https://store-usa.arduino.cc/products/nano-esp32) 开发板,做了一些基础项目——闪灯、ESPNow 乒乓通信等等。虽然有趣,但要用 ESP32 做点有用的“工作”,还得精通电子电路,而这并非我的强项。
经过一番搜索,我最终锁定了 Game Boy Advance 作为开发目标。它搭载 32 位 ARM 处理器,自带输入按键,有屏幕和扬声器输出,支持 MMIO(内存映射输入/输出),并且标准化程度很高——在一台 GBA 上能运行的程序,在其他任何 GBA 上也能运行。与此同时,它也有诸多严格限制:内存有限、ROM 空间有限、VRAM 限制、精灵数量限制等。
当时我脑海中浮现出奥逊·威尔斯的这句话:
> “艺术的敌人是缺乏限制。” —— 奥逊·威尔斯
于是我开始探索这个领域已有的成果。我找到了两个目标不同的 crate:
第一个是 [gba](https://docs.rs/gba/latest/gba/)(docs.rs/gba/latest/gba/)——它定位为“仅提供足够安全且能操作硬件的 Rust API”。
它的文档描述得最清楚:
> 本 crate 提供了与 GBA 交互的 API,这种 API 是安全的,但对何时能修改哪些组件的限制极少。如果你想要借出检查器对组件访问进行更强控制的 API,那么 [agb](https://docs.rs/agb/latest/agb/)(docs.rs/agb/latest/agb/)可能更适合你。
这段话打动了我,因为我想要尽可能少“手把手指导”的底层 Rust 体验。于是我深入研究了 `gba` crate。
接着我阅读了仓库,搭建了项目,并跟随其他优秀开发者的教程开始 GBA 开发。
我跟随的第一个教程是 [Shane 的博客——“在 Rust 中构建 GBA 游戏”](https://shanesnover.com/2024/02/07/intro-to-rust-on-gba.html)(shanesnover.com/2024/02/07/intro-to-rust-on-gba.html),其中介绍了 Shane 学习 Rust GBA 开发的过程。最棒的是他提供了 [“Conway 生命游戏”的仓库](https://github.com/ssnover/game-of-life)(github.com/ssnover/game-of-life),里面有一个可运行的 GBA Rust 项目,这成为我后续 GBA 游戏的基石(我们也会从中借鉴一些功能和代码)。这篇博文值得一读,它从高层次介绍了 GBA 开发,同时又以“生命游戏”提供了具体示例。
此后我主要参考的是 [Kyle Halladay 的“GBA 教程”](https://kylehalladay.com/gba.html)(kylehalladay.com/gba.html)系列博文,内容涵盖如何用 C++ 开始 GBA 开发:从在屏幕上绘图,到绘制精灵、绘制背景层、处理用户输入等。这是一份绝佳资源,强烈建议你有空时读一读。本教程将以类似的顺序和风格介绍相关内容,你会发现它在节奏和方向上给了我很大启发。
另一份深入硬件层面、解释 GBA 实际工作原理的优质资源是 [Tonc](https://www.coranac.com/tonc/text/toc.htm)(coranac.com/tonc/text/toc.htm)。虽然阅读起来有些难度(非常技术性),但它包含了大量信息:底层硬件、内存布局、特性、技巧、指针、代码示例等。每当需要理解新系统(如精灵或背景模式)时,我喜欢阅读 Tonc 中关于每种模式的说明、能做什么,以及如何最佳利用它们的代码示例。建议将那个网站加入书签,里面信息丰富,将来会有大用。
本教程会重新涉及 Kyle 教程中的许多内容,但将完全专注于 Rust 和 `gba`(https://docs.rs/gba/latest/gba/)crate。由于我们在嵌入式系统上工作,我们只能使用 `core`(也可以使用 `alloc`,但本项目不会用到),而无法使用传统的、庞大的 Rust 标准库。
本教程将涵盖入门所需的基础知识,后续可能会推出涵盖更高级主题的教程。
学完本教程后,你将拥有一个能构建出可运行 GBA ROM 的 Rust 项目,可作为自己游戏的起点。
## 搭建开发环境(https://jonahnestrick.com/blog/rust-gba-tutorial-1/#setup)
#### Rust
开始之前需要满足几个前提条件。
你的机器上需要安装 Rust/Cargo。我们将使用 nightly 版本的编译器。如果你已安装了较新的 Rust/Cargo,稍后配置项目时它会自动下载所需的 nightly 版本(感谢 `rust-toolchain.toml`)。
如果尚未安装 Rust,可以使用 [RustUp](https://rustup.rs/)(rustup.rs)进行安装。
#### 其他软件
接下来,我们将按照 [gba](https://docs.rs/gba/latest/gba/#how-to-make-your-own-gba-project-using-this-crate)(docs.rs/gba/latest/gba/)crate 的说明来搭建自己的 GBA 项目。这里需要下载并配置一些东西,但只需一次性设置。
##### ARM Binutils
首先需要下载 [ARM Binutils](https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain)(developer.arm.com/Tools%20and%20Software/GNU%20Toolchain)。
该网站有些复杂,包含许多下载选项,你需要按以下条件筛选安装程序:
1. 操作系统(Windows、Linux、Mac)
2. 架构:Intel (x86) 或 ARM (aarch),32 位或 64 位等。
3. 工具链:我们寻找的是“裸机目标(`arm-none-eabi`)”
安装完成后,需要修改 PATH 环境变量,使 ARM 工具中的 `arm-none-eabi-ld` 二进制文件能被找到。
在 Windows 上,路径通常是:`C:\Program Files\Arm\GNU Toolchain mingw-w64-x86_64-arm-none-eabi\bin`
如何在 Windows 上设置 PATH 变量(https://www.architectryan.com/2018/03/17/add-to-the-path-on-windows-10/)
在 Mac 和 Linux 上,需要更新 shell 环境文件以指向这些工具。如果使用了包管理器,通常可以跳过此步骤。
##### GBA 模拟器
最后我们需要一个模拟器来运行我们的游戏。根据 `gba` crate 的说明,该 crate 生成的 ROM 可以直接在真机上运行,但需要额外步骤,本教程不会涉及。
我们将使用 [mGBA 模拟器](https://mgba.io/downloads.html)(mgba.io/downloads.html),这是 crate 作者推荐的模拟器。
## 项目创建(自动方式)(https://jonahnestrick.com/blog/rust-gba-tutorial-1/#project-creation-auto)
下面的教程部分将介绍如何搭建 Rust 项目并进行正确配置。
如果你想跳过手动创建文件的过程,可以使用以下命令克隆本项目的起始副本:
```
git clone -b starter https://github.com/undecidedapollo/gba-tutorial.git
```
克隆完成后,你需要编辑 `./cargo/config.toml`,使其指向上面下载的 `mGBA` 模拟器路径,不同操作系统有所不同。
更多信息请参见下面的“Cargo 配置”(https://jonahnestrick.com/blog/rust-gba-tutorial-1/#cargo-config)
克隆并更新 `./cargo/config.toml` 后,你应该能够运行 `cargo run`,它会启动 `mGBA` 并显示一个空白的白色屏幕。
点击此处直接跳转到“做点什么”(https://jonahnestrick.com/blog/rust-gba-tutorial-1/#doing-something)
## 项目创建(手动方式)(https://jonahnestrick.com/blog/rust-gba-tutorial-1/#project-creation-manual)
一切安装完毕后,我们可以使用 `cargo` 创建一个新的 `binary` 项目,作为构建 GBA 游戏的基础。
```
cargo new --bin gba-tutorial
```
然后 `cd gba-tutorial` 进入项目目录,开始为 GBA 开发进行配置。
#### Nightly Rust
使用 Rust 进行 GBA 开发需要使用 nightly 编译器。为了让 Rust 始终为本项目使用 nightly 编译器,我们需要在项目根目录下创建一个 `rust-toolchain.toml` 文件。
```
[toolchain]
channel = "nightly"
components = ["rust-src"]
```
如果你和我一样喜欢固定版本,可以在此处指定一个特定的 nightly 版本:
```
[toolchain]
channel = "nightly-2026-05-09"
components = ["rust-src"]
```
#### Cargo 配置(https://jonahnestrick.com/blog/rust-gba-tutorial-1/#cargo-config)
首先,我们需要向 cargo 提供更多关于本项目及其构建方式的信息。创建一个名为 `.cargo` 的目录,并在其中创建名为 `config.toml` 的文件(完整路径:`.cargo/config.toml`)。你需要修改文件中的某些值,我在下面标出了它们,注释中有更多信息。
runner 变量需要指向你系统中 mGBA 可执行文件的位置。根据安装方式(下载、包管理器等)不同,你需要更新 runner 使其指向该可执行文件。
下面我列出了一些常见配置,你可以取消注释(删除你选择的 `runner = "MGBA_LOCATION"` 前面的 `#` 符号)来使用。
```
[build]
target = "thumbv4t-none-eabi" # 指定目标 CPU/系统架构
[unstable]
build-std = ["core"] # 指定我们只需要 core
[target.thumbv4t-none-eabi]
# 取消注释适用于你平台的 runner
# Windows:指向已安装的 exe
# runner = ["C:\\Program Files\\mGBA\\mGBA.exe"]
# MacOS:指向 .app 文件内的可执行文件
# runner = ["/Applications/mGBA.app/Contents/MacOS/mGBA"]
# Linux:如果通过包管理器安装
# runner = ["mgba-qt"]
rustflags = [
"-Clinker=arm-none-eabi-ld", # 使用 ARM 链接器
"-Clink-arg=-Tlinker.ld", # 设置链接脚本
]
```
#### 下载链接脚本
我们需要一个链接脚本来告诉链接器如何正确组织可执行文件的结构。幸好 `gba` crate 的作者为我们提供了一个。
[链接器文件](https://github.com/rust-console/gba/blob/main/linker_scripts/mono_boot.ld)(github.com/rust-console/gba/blob/main/linker_scripts/mono_boot.ld)
下载或复制该文件,并保存到项目目录下的 `linker.ld` 文件中。
#### 添加 GBA Crate
向项目添加 gba crate,我们将使用 0.15 版本(本教程编写时的最新版本)。
你可以使用 cargo 添加:
```
cargo add [email protected]
```
最终你的 `Cargo.toml` 应如下所示:
```
[package]
name = "gba-tutorial"
version = "0.1.0"
edition = "2024"
[dependencies]
gba = "0.15"
```
#### 创建第一个可执行文件
最后,我们从 `gba` crate 文档中复制一个修改过的启动函数定义,它创建了一个兼容 `no_std` 的可执行文件。这个文件很简陋,什么也不做,但我们可以运行它。
将已有的 `src/main.rs` 文件(上面通过 `cargo new` 创建)更新为以下内容:
```
#![no_std]
#![no_main]
use gba::prelude::*;
#[panic_handler]
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
loop {}
}
#[unsafe(no_mangle)]
extern "C" fn main() -> ! {
loop {}
}
```
#### (可选)VSCode 配置
如果你在 VSCode 中使用 rust-analyzer,可能会发现它对上述代码修改给出警告。因为我们构建的 ROM 不使用 std,并且采用自定义架构,需要告知 VSCode。为此,创建一个名为 `.vscode` 的文件夹,并在其中创建名为 `settings.json` 的文件,写入以下内容(完整路径:`.vscode/settings.json`):
```json
{
"rust-analyzer.cargo.target": "thumbv4t-none-eabi",
"rust-analyzer.cargo.buildScripts.overrideCommand": [
"cargo", "check", "--target", "thumbv4t-none-eabi",
"-Z", "build-std=core,alloc",
"--message-format=json"
],
"rust-analyzer.check.overrideCommand": [
"cargo", "check", "--target", "thumbv4t-none-eabi",
"-Z", "build-std=core,alloc",
"--message-format=json"
],
"rust-analyzer.check.allTargets": false,
"rust-analyzer.cargo.extraEnv": {
"RUSTFLAGS": "-Clink-arg=-Tlinker.ld"
}
}
```
#### 首次运行我们的 ROM
至此,我们可以运行 ROM 了!
```
cargo run --release
```
我们使用 `--release` 模式,因为调试模式下生成的 Rust 代码未优化,GBA 的 ARM 处理器速度太慢,无法以合理速度运行代码。
稍后当我们绘制一个矩形时,可以尝试在调试模式下运行(不加 release 标志),看看填充屏幕的速度有多慢。
虽然它目前只显示一个白色屏幕,但这证明了我们可以将 Rust 程序构建成 Game Boy Advance ROM。
(图片:mGBA 模拟器显示白色背景)
## 做点什么(https://jonahnestrick.com/blog/rust-gba-tutorial-1/#doing-something)
现在我们有了一个能运行的起始项目,试着在 GBA 屏幕上显示一些东西吧。
### GBA 视频背景基础
Game Boy Advance 拥有一块 240(水平)× 160(垂直)的彩色屏幕,可以通过多种视频模式驱动。每种视频模式在控制屏幕方面提供了不同的特性组合。GBA 有几个不同的特性可以用来在屏幕上显示内容。
#### 高级特性概览
**背景特性**
- **位图背景**:控制屏幕上的单个像素,速度较慢。
- **文本背景**:传统的基于瓦片的布局,瓦片是 8×8 像素的组块,可以以网格形式放置在屏幕上。
- **仿射背景**:高级背景,类似于基于瓦片的布局,但可以对整个背景进行操作,如缩放、旋转和剪切。
**前景特性**
- **对象/精灵**:128
相似文章
为什么我在2024年用Zig编写了一个Game Boy Advance游戏
一位开发者解释了为什么他们选择Zig编程语言来创建Game Boy Advance游戏,强调了Zig的交叉编译能力及其对嵌入式编程的适用性。
Show HN: 我花了一个周末用Rust重写了IDE中我唯一使用的部分
Kyde 是一个快速的原生Git客户端和代码编辑器,使用Rust基于Zed的gpui框架构建,具有GPU渲染、并排差异对比、tree-sitter语法高亮以及一个精心调校的深色主题。
使用Rust解析Godot .tres文件并遍历资源图
本文详细介绍了在Rust中为Asset Hoard资产管理器实现.tres文件解析和资源图遍历的过程,支持Godot项目的外部依赖解析和拖放导出。
在iPhone上编程GBA游戏
一位作者记录了如何完全在iPhone上编程Game Boy Advance游戏,使用了iSH、Textastic、Delta和gba bootstrap等工具,最终制作了一款名为TO THE TOWER的短小游戏。
从Go迁移到Rust
一份为Go开发者迁移到Rust编写的全面指南,专注于后端服务,对比正确性、运行时和人体工程学方面的权衡,并提供关于渐进式迁移的实用建议。