Miscellaneous

This article covers topics which aren't big enough to be their own section, but are worth covering in a smaller section here.

Printing to the console

If your game is running under the mGBA emulator then you can print to the console using the agb::println! macro. So to print the text Hello, World!, you would do the following:


#![allow(unused)]
fn main() {
agb::println!("Hello, World!");
}

The println! macro works the same way as the standard library println! macro. However, you should note that formatting arguments is quite slow, so the main use for this is debugging, and you'll probably want to remove them when actually releasing your game as they can make it so that your game logic doesn't calculate within a frame any more.

Random numbers

agb provides a simple random number generator in agb::rng. To generate a random number, you can either create your own instance of a RandomNumberGenerator, or use the global next_i32() method.

The Game Boy Advance has no easy way of seeding the random number generator, and creating random numbers which are different for each boot can be quite difficult. One thing you can do to make random numbers harder to predict is to call the next_i32() method once per frame.


#![allow(unused)]
fn main() {
loop {
    let mut frame = gfx.frame();
    // do your game rendering

    frame.commit();

    // make the random number generator harder to predict
    let _ = agb::rng::next_i32();
}
}

HashMaps

alloc does not provide a HashMap implementation, and although you can import the no_std version of hashbrown, it can be quite slow on the Game Boy Advance due to its use of simd and other intrinsics which have to be emulated in software.

Therefore, agb provides its own HashMap and HashSet implementations which are optimised for use on 32-bit embedded devices which you can use from the agb::hash_map module. These work exactly like the HashMap from the standard library, providing most of the same API, with only a few omissions for rarely used methods or ones which don't make sense with the different backing implementation.

Allocators

By default, all allocations in agb go into the more plentiful, but slower EWRAM (Extended Working RAM). EWRAM is 256kB in size, which is normally more than enough for even a moderately complex game. Reading and writing to EWRAM takes 3 CPU cycles per access, so is fairly slow however.

If you have a collection you're reading and writing to a lot, you can instead move it to the faster, but smaller IWRAM (Internal Working RAM). IWRAM is only 32kB in size and is used for the stack, along with some high performance code, so you should be careful with what you put there. However, it does only take 1 CPU cycle to read or to write to, so it can be noticeably faster to use.

To allocate in IWRAM, use the new_in() methods on Vec, Box or HashMap.


#![allow(unused)]
fn main() {
use agb::InternalAllocator;
use alloc::vec::Vec;

let mut v = Vec::new_in(InternalAllocator);
v.push("Hello, World!");
}