返回 Skill 列表
extension
分类: 开发与工程无需 API Key

rust-gba-dev

Comprehensive skill for developing Game Boy Advance (GBA) games using Rust programming language with the agb framework. Use when users want to (1) create GBA games with Rust, (2) set up GBA Rust development environment, (3) use the agb crate and framework, (4) compile Rust code to GBA ROMs, (5) work with GBA hardware abstractions (graphics, sound, input), (6) debug or emulate GBA Rust games, or (7) learn about Rust GBA development resources and best practices. Also triggers for questions about agb crate, GBA sprites, backgrounds, audio mixer, tracker music, or ARM thumbv4t development.

person作者: awol2005exhubModelScope

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 .xm files
  • Easy-to-use sprite and tiled background system
  • Global allocator supporting both core and alloc

Quick Start

Environment Setup

Prerequisites:

  1. 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
    
  2. Install ARM GBA Target:

    rustup target add thumbv4t-none-eabi
    
  3. Install agb-gbafix (for real hardware deployment):

    cargo install agb-gbafix
    
  4. Install Emulator (recommended: mGBA):

    • Download from https://mgba.io
    • Ensure mgba-qt is in your PATH for cargo run support

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 .aseprite file (native format supported by agb)

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 .xm format
  • 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 alloc crate)
  • 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:

  • agb provides a panic handler that displays error info on screen
  • Useful for debugging on real hardware

VSCode debugging:

  • See book/src/articles/vscode_debugger.md in 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/ and examples/ directories

Troubleshooting

Common Issues

  1. Linker errors: Ensure thumbv4t-none-eabi target is installed

    rustup target add thumbv4t-none-eabi
    
  2. Black screen:

    • Check that frame.commit() is called
    • Ensure VBlank is being waited for
  3. No audio:

    • Verify mixer.frame() is called each frame
    • Check sound file format and sample rate
  4. Performance issues:

    • Use release build: cargo build --release
    • Minimize heap allocations
    • Use fixed-point math instead of floats
  5. 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

  1. Follow the tutorial: https://agbrs.dev/book/

    • Part I: Pong game (sprites, input, collision)
    • Part II: Platformer (backgrounds, levels, physics)
  2. 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
  3. 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.