Rust GBA Development with agb Framework
Overview
agb is a powerful Rust library for creating Game Boy Advance games. It provides high-level abstractions for GBA hardware (graphics, audio, input) while maintaining safety through Rust's type system. The framework handles low-level details, allowing you to focus on game logic.
Key Features:
- Simple build process with minimal dependencies
- Built-in asset importing (sprites, backgrounds, music, sound effects)
- High-performance audio mixer with optional tracker support for
.xmfiles - Easy-to-use sprite and tiled background system
- Global allocator supporting both
coreandalloc
Quick Start
Environment Setup
Prerequisites:
-
Install Rust (latest stable/nightly):
# Windows: Download and run rustup-init.exe from https://rustup.rs # Or through PowerShell: irm https://sh.rustup.rs | sh -
Install ARM GBA Target:
rustup target add thumbv4t-none-eabi -
Install agb-gbafix (for real hardware deployment):
cargo install agb-gbafix -
Install Emulator (recommended: mGBA):
- Download from https://mgba.io
- Ensure
mgba-qtis in your PATH forcargo runsupport
Creating a New Project
Use the official template:
# Clone the template
git clone https://github.com/agbrs/template.git my-gba-game
cd my-gba-game
# Or use cargo-generate (if installed)
cargo generate --git https://github.com/agbrs/template
Template structure:
my-gba-game/
├── Cargo.toml # Dependencies include agb crate
├── src/
│ └── main.rs # Entry point with #[agb::entry]
├── gfx/ # Aseprite sprites, backgrounds
├── sfx/ # WAV sound effects, XM music
├── .cargo/ # Configuration for GBA target
└── rust-toolchain.toml # Specifies nightly toolchain
Building and Running
Build the project:
# Debug build
cargo build
# Release build (recommended for distribution)
cargo build --release
Output location:
- Debug:
target/thumbv4t-none-eabi/debug/<game-name> - Release:
target/thumbv4t-none-eabi/release/<game-name>
Run in emulator:
# If mgba-qt is in PATH
cargo run
# Or manually
mgba target/thumbv4t-none-eabi/debug/<game-name>
Create .gba file (for real hardware):
agb-gbafix target/thumbv4t-none-eabi/release/<game-name> -o <game-name>.gba
Core Concepts
Entry Point and the Gba Struct
Every agb game starts with a function annotated with #[agb::entry]:
#![no_std]
#![no_main]
use agb::{Gba, interrupt::VBlank};
#[agb::entry]
fn main(mut gba: Gba) -> ! {
// Your game loop here
loop {
// Wait for VBlank to avoid screen tearing
VBlank::wait();
}
}
The Gba struct:
- Passed to your main function (don't create it yourself)
- Gateway to all GBA hardware:
gba.graphics(),gba.mixer(),gba.input() - Enforces safe hardware access through Rust's type system
Game Loop Structure
#[agb::entry]
fn main(mut gba: Gba) -> ! {
// Initialize systems
let mut gfx = gba.graphics.get();
let mut mixer = gba.mixer.mixer(agb::sound::mixer::Frequency::Hz18157);
let mut input = agb::input::ButtonController::new();
// Load assets
let ball_sprite = include_aseprite!("gfx/sprites.aseprite");
// Game state
let mut ball_x = 50;
let mut ball_y = 50;
loop {
// 1. Update input
input.update();
// 2. Handle input
if input.is_pressed(agb::input::Button::A) {
// Action
}
// 3. Update game logic
ball_x += 1;
// 4. Render
let mut frame = gfx.frame();
// Draw sprites
let mut ball = Object::new(ball_sprite.sprite(0));
ball.set_pos((ball_x, ball_y));
ball.show(&mut frame);
// 5. Commit frame (waits for VBlank)
frame.commit();
// 6. Update audio
mixer.frame();
}
}
Graphics Programming
Sprites (Objects)
Import sprite data:
use agb::include_aseprite;
include_aseprite!(
mod sprites,
"gfx/sprites.aseprite"
);
// Creates `sprites::BALL`, `sprites::PADDLE`, etc. (based on Aseprite tags)
Create and display sprites:
use agb::display::object::Object;
// Create sprite
let mut ball = Object::new(sprites::BALL.sprite(0));
// Set position
ball.set_pos((100, 80));
// Show in frame
let mut frame = gfx.frame();
ball.show(&mut frame);
frame.commit();
Sprite sizes: 8x8, 16x16, 32x32, 64x64 (or rectangular combinations)
Backgrounds
Loading background data:
use agb::include_background;
include_background!(
mod bg,
"gfx/background.png"
);
Displaying backgrounds:
let mut background = gfx.background(agb::display::PRIORITY_0);
background.set_map(bg::MAP);
background.show();
Graphics Frame
The GraphicsFrame handles double-buffering and VBlank timing:
loop {
let mut frame = gfx.frame(); // Start new frame
// Draw all objects for this frame
sprite1.show(&mut frame);
sprite2.show(&mut frame);
frame.commit(); // Wait for VBlank and display
}
Input Handling
ButtonController
use agb::input::{ButtonController, Button};
let mut button_controller = ButtonController::new();
loop {
button_controller.update(); // Call once per frame
// Check button states
if button_controller.is_pressed(Button::A) {
// Held down
}
if button_controller.is_just_pressed(Button::A) {
// Just pressed this frame
}
if button_controller.is_just_released(Button::A) {
// Just released this frame
}
// D-pad directions
let x = button_controller.x_tri(); // Tri::Negative, Zero, Positive
let y = button_controller.y_tri();
// Combine buttons
if button_controller.is_pressed(Button::A | Button::B) {
// A or B pressed
}
}
GBA Buttons: D-pad, A, B, L, R, Start, Select
Emulator key mapping (default):
- D-pad: Arrow keys
- A: X, B: Z
- Start: Enter, Select: Backspace
- L: A, R: S
Sound and Music
Audio Mixer
Initialize mixer:
use agb::sound::mixer::{Mixer, Frequency};
let mut mixer = gba.mixer.mixer(Frequency::Hz18157);
// Options: Hz10512 (low quality), Hz18157 (recommended), Hz32768 (high quality)
Import sound effects:
use agb::include_wav;
static EXPLOSION: SoundData = include_wav!("sfx/explosion.wav");
Play sounds:
use agb::sound::mixer::SoundChannel;
let mut channel = SoundChannel::new(EXPLOSION);
channel.volume(0.5.into()); // 50% volume
channel.stereo(); // Enable stereo
mixer.play_sound(channel);
Mixer must be updated each frame:
loop {
let mut frame = gfx.frame();
// ... rendering ...
mixer.frame(); // Update audio
frame.commit();
}
Tracker Music (XM files)
Add dependency: agb_tracker = "0.2" in Cargo.toml
Import and play tracker music:
use agb_tracker::{Track, Tracker, include_xm};
static BGM: Track = include_xm!("sfx/music.xm");
let mut tracker = Tracker::new(&BGM);
loop {
let mut frame = gfx.frame();
// Step tracker (plays samples)
tracker.step(&mut mixer);
mixer.frame();
frame.commit();
}
Asset Preparation
Sprites and Backgrounds
Required tool: Aseprite (can compile from source for free)
- Create sprites in Aseprite
- Use tags to define individual sprites
- Export as
.asepritefile (native format supported byagb)
Sound Effects
Required tool: FFmpeg (for resampling)
# Resample audio to target frequency
ffmpeg -i input.mp3 -ar 18157 sfx/effect.wav
Supported format: WAV (uncompressed PCM)
Music
Required tool: MilkyTracker or OpenMPT
- Compose music in tracker software
- Export as
.xmformat - Significantly smaller than WAV (kB vs MB)
Advanced Topics
Fixed-Point Math
The GBA lacks floating-point hardware. Use agb-fixnum for decimal numbers:
use agb::fixnum::Frac;
let x: Frac<i32, 8> = Frac::from(1.5); // 1.5 with 8 fractional bits
let y = x * 2; // Fixed-point arithmetic
Memory Management
- Use
#![no_std]to avoid standard library - Global allocator is available (can use
alloccrate) - Be mindful of GBA's limited RAM (384KB)
Debugging
With mGBA:
- Use
agb::println!()for debug output (requires mGBA) - mGBA logs appear in emulator console
Panic screen:
agbprovides a panic handler that displays error info on screen- Useful for debugging on real hardware
VSCode debugging:
- See
book/src/articles/vscode_debugger.mdin the agb repo
Project Structure (Template)
my-gba-game/
├── Cargo.toml # agb dependency, build config
├── src/
│ ├── main.rs # Entry point
│ ├── game.rs # Game logic
│ └── assets.rs # Asset loading (optional)
├── gfx/
│ ├── sprites.aseprite # Sprite sheet
│ └── background.png # Background image
├── sfx/
│ ├── jump.wav # Sound effects
│ └── music.xm # Tracker music
├── .cargo/
│ └── config.toml # Linker configuration
└── rust-toolchain.toml # Specifies nightly toolchain
Resources
Official Documentation
- Website: https://agbrs.dev/
- GitHub: https://github.com/agbrs/agb
- API Docs: https://docs.rs/agb
- Book/Tutorial: https://agbrs.dev/book/ (in-repo:
book/src/) - Discussions: https://github.com/agbrs/agb/discussions
Tools
- agb-image-converter: Converts images to GBA format (built into
agb) - agb-sound-converter: Converts audio (built into
agb) - agb-gbafix: Converts ELF to .gba file
- agb-debug: Decodes stack traces
- agb-fixnum: Fixed-point math library
Emulators
- mGBA (recommended): https://mgba.io - Best accuracy and debugging
- No$GBA: Good for low-level debugging
- VisualBoyAdvance: Older but widely used
Example Games
- Built with
agb: https://itch.io/c/4302342/games-made-with-agb - In-repo examples:
agb/examples/andexamples/directories
Troubleshooting
Common Issues
-
Linker errors: Ensure
thumbv4t-none-eabitarget is installedrustup target add thumbv4t-none-eabi -
Black screen:
- Check that
frame.commit()is called - Ensure VBlank is being waited for
- Check that
-
No audio:
- Verify
mixer.frame()is called each frame - Check sound file format and sample rate
- Verify
-
Performance issues:
- Use release build:
cargo build --release - Minimize heap allocations
- Use fixed-point math instead of floats
- Use release build:
-
Asset importing fails:
- Ensure Aseprite files are in correct format
- WAV files must match mixer frequency
Getting Help
- Discussions: https://github.com/agbrs/agb/discussions
- GBAdev Discord: Friendly community for GBA development
- Issues: https://github.com/agbrs/agb/issues
Next Steps
-
Follow the tutorial: https://agbrs.dev/book/
- Part I: Pong game (sprites, input, collision)
- Part II: Platformer (backgrounds, levels, physics)
-
Read advanced articles (in
book/src/articles/):- Frame lifecycle
- Backgrounds deep dive
- Objects deep dive
- Blending and windows
- DMA (Direct Memory Access)
- Saving and loading
-
Explore examples:
# From the agb repository just run-example <example-name>
Note: This skill is based on agb framework version 0.2.x. Check the official documentation for the most up-to-date API and features.
微信扫一扫