Larold’s Jubilant Junkyard” has become “Larold’s Retro Gameyard“. I’ve been working on this re-branding and migration for a while. All old tutorials should redirect you to this new site. If you see any errors, please let me know! See here for more information: What happened to Larold’s Jubilant Junkyard?

Drawing sprites for game boy advance with butano game engine

Drawing sprites on the Game Boy Advance

Sprites are used to represent more of the objects in video games. We can move them around the screen, animate them, and more.

All graphics (sprites & backgrounds) for the Butano Game engine must be bitmap images. These images must be placed in the “graphics” folder of your project. No changes need to be added to the makefile. However, like mentioned in my previous tutorial on Project Structure in Butano, a matching .json file will be needed.

The Butano Game Engine has three shapes for sprites, and four sizes for each shape. Here’s a table of all the possibilities for Shape & Size. Your image must be one of the following sizes:

Shape / SizeSmall Normal BigHuge
Square8×816×1632×3264×64
Wide 16×832×832×1664×32
Tall8×168×3216×3232×64

For each bitmap asset for sprites, Butano will create a constant object for that asset under the “bn::sprite_items” namespace.

I’m using a graphic from the Butano Mosaic example:

I’ve renamed it to “testplayer.bmp“, and created a matching “testplayer.json” file:

Important Note: All graphics (sprites and backgrounds) must have all lowercase names. Also if you see an error, like “testplayer.bmp error: Invalid bits per pixel: 24” make sure you are exporting with an indexed palette.

{
    "type": "sprite"
}

Creating the sprite

To get your sprite on screen you need to include two things:

  1. The sprite pointer class
  2. The generated sprite items class

Since my bitmap was named “testplayer.bmp”, after re-running the “make” command, butano has generated a matching sprite items header file “bn_sprite_items_testplayer.h”.

#include <bn_sprite_ptr.h>
#include "bn_sprite_items_testplayer.h"

With those included, you need to now create the sprite item. This must be done AFTER the call to “bn::core::init(). The following, creates a sprite at x=0, y=0;

// This must be done AFTER the call to "bn::core::init()"
bn::sprite_ptr sprite = bn::sprite_items::testplayer.create_sprite(0, 0);

After that, your sprite will be visible. Without any other adjustments, (0,0) will place your sprite in the middle of the screen.

The Game Boy Advance supports up to 128 different sprites. You can even adjust their render order by using their “bn::sprite_ptr::set_z_order(int)” or “bn::sprite_ptr::set_bg_priority(int)” functions.

Moving, Scaling, and Rotating Sprites

Unlike with the Game Boy, with the Game Boy Advance & Butano Game Engine we can also rotate, shear, and scale our sprites.

Moving sprites

We can get our sprites position using the bn::sprite_ptr::x() and bn::sprite_ptr::y() functions. Additionally, we can change those values using the matching bn::sprite_ptr::set_x(fixed x) and bn::sprite_ptr::set_y(fixed y) functions.

Note: All four of these functions use Butano’s bn::fixed type. The GBA doesn’t have dedicated hardware for floats/doubles. While they can be emulated in software, that is very slow and not recommended. More Info.

Here’s an example how to make our sprite move horizontally

bn::fixed spriteX = sprite.x();

spriteX+=2;

sprite.set_x(spriteX);

Scaling Sprites

You can pass bn::fixed values to the bn::sprite_ptr::set_horizontal_scale and bn::sprite_ptr::set_vertical_scale functions to change the size of your sprite.

bn::fixed horizontalScale = sprite.horizontal_scale();
bn::fixed verticalScale= sprite.vertical_scale();

horizontalScale += bn::fixed(0.01);
verticalScale+= bn::fixed(0.03);

sprite.set_horizontal_scale(horizontalScale);
sprite.set_vertical_scale(verticalScale);

Which will look like this:

There Are limits though. If you go equal to, or below zero, you’ll see a error in your emulator:

If you go too big, your sprite will be limited. The maximum sprite canvas size is double the original size of the sprite. for example: If your sprite is 64×64, the max canvas size is 128×128. In the below screenshot, my 64×64 sprite has gone beyond that limit, and becomes clipped.

Rotating Sprites

You can get & set a sprites rotation using the “bn::sprite_ptr::rotation_angle()” and “bn::sprite_ptr::set_rotation_angle_safe(bn::fixed)” functions respectively. The value must be a bn::fixed lke the previous functions, and in degrees (not radians)

bn::fixed rotation = sprite.rotation_angle();

rotation+=bn::fixed(0.5);

sprite.set_rotation_angle_safe(rotation);

When you compile and run, you’ll se your sprite happily rotating.

Note: there is a “bn::sprite_ptr::set_rotation_angle(bn::fixed)” function, but you will it explicitly expects a value between 0 and 360. If you provide a value outside of that range, you’ll see an error like this:

You can also flip sprites horizontally and vertically using the “bn::sprite_ptr::set_horizontal_flip(bool)” and “bn::sprite_ptr::set_vertical_flip(bool)” functions.

Shearing Sprites (Also called “Skewing”)

In addition to rotation, translation, and scaling; you can also “shear” sprites. This is where you slant the sprite along it’s horizontal or vertical axis. This is called “Skewing” in some other programs. This is done via the following functions:

If you apply a constant horizontal skew:

bn::fixed shear = sprite.horizontal_shear();

shear += bn::fixed(.5);

sprite.set_horizontal_shear(shear);

Which looks like this:

Removing/Hiding sprites

Sprites will automatically be removed, when their pointer goes out of context. If your sprite remains on screen, then SOMEWHERE exists a pointer to it still. You can temporarily hide a sprite, and still keep it alive. use the “bn::sprite_ptr::set_visible(bool)” function. Here’s an example of a sprite being removed, when it goes out of context.

#include <bn_core.h>
#include <bn_keypad.h>
#include <bn_sprite_ptr.h>
#include "bn_sprite_items_testplayer.h"


int main()
{
    bn::core::init();

    {
        
        bn::sprite_ptr sprite = bn::sprite_items::testplayer.create_sprite(0, 0);

        // Wait for a to be presseed
        while(!bn::keypad::a_pressed()){bn::core::update();}
    }

    // The "sprite" is out of context, and will automatically be removed
    
}

Important Reminder: Do not try to create sprite pointers before the call to “bn::core::init()”

Alternatively, you can use “bn::optional” class or contain the sprite pointers in a “bn::vector“.

Using Optionals for sprite management

The “bn::optional” class is another way to get rid of sprites. You can give it a value using the “=” operator. You can remove it’s value using it’s reset function. Use it’s “has_value” function to determine if it has an actual value or not.

To directly access it’s target object , use the arrow operator “->”. To access the optional itself, use the dot operator “.”. Here’s an example below. When A is pressed, the optional is given a value. When B is pressed, the optional’ s value is removed (thereby removing the sprite). If the optional has a value, the sprite will be visible and moving across the screen

#include <bn_core.h>
#include <bn_keypad.h>
#include <bn_sprite_ptr.h>
#include "bn_sprite_items_testplayer.h"


int main()
{
    bn::core::init();

    // This is an optional sprite_ptr
    // It can OPTIONALLY have a sprite_ptr value
    // use the '.' operator to access data/functions on the optional
    // Use the '->' operator to access data/functions on the sprite pointer itself
    bn::optional<bn::sprite_ptr> optionalSprite;

    // Wait for a to be presseed
    while(true){
        
        bn::core::update();

        // if we pressed a, and our optional does not have an actual value
        // use the '.' operator to access data/functions on the optional, and not the sprite pointer itself
        if(bn::keypad::a_pressed() && !optionalSprite.has_value()){

            // Give our optional a value
            // use the '.' operator to access data/functions on the optional, and not the sprite pointer itself
            optionalSprite = bn::sprite_items::testplayer.create_sprite( 0, 0);

        }

        // if we pressed b, and our optional has an actual value
        // use the '.' operator to access data/functions on the optional, and not the sprite pointer itself
        if(bn::keypad::b_pressed() && optionalSprite.has_value()){

            // Clear our optional sprite, leaving it with no value
            // use the '.' operator to access data/functions on the optional, and not the sprite pointer itself
            // The sprite will automatically be removed from the screen
            optionalSprite.reset();
        }

        // Do we have an value in our optional?
        // use the '.' operator to access data/functions on the optional, and not the sprite pointer itself
        if(optionalSprite.has_value()){

            // Get the x position of the sprite
            // Use -> to directly access the sprite data/functions, and not the optional data/functions
            bn::fixed x = optionalSprite->x();

            x+=bn::fixed(2);

            // Update the position of the sprite
            // Use -> to directly access the sprite data/functions, and not the optional data/functions
            optionalSprite->set_x(x);
        }
        
        
    }
}

The result. Each time i press b the sprite disappears. When i press a, it re-appears at the start and moves rightward.

Using Vectors for sprite management

A bn::vector, is similar to your traditional c++ vector class. If you push your sprite pointer directly into it. When you remove that sprite pointer from the vector, the sprite will automatically be removed from the screen.

#include <bn_core.h>
#include <bn_random.h>
#include <bn_keypad.h>
#include <bn_vector.h>
#include <bn_sprite_ptr.h>
#include "bn_sprite_items_testplayer.h"

int main()
{
    bn::core::init();

    // From: #include <bn_random.h>
    bn::random r;

    // A vector of sprite pointers, maximum size: 1
    // From: #include "bn_vector.h"
    bn::vector<bn::sprite_ptr,10> sprites;

    // Wait for a to be presseed
    while(true){
        
        bn::core::update();

        // if we dont have 10 sprites and a is pressed
        if(bn::keypad::a_pressed() && sprites.size()<10){

            // Figure out a random position
            int randomX = -120+r.get()%240;
            int randomY = -80+r.get()%160;

            // Add to an vector of sprite points
            sprites.push_back(bn::sprite_items::testplayer.create_sprite( randomX, randomY));
        }

        // When b is presssed, and if we have any sprites
        if(bn::keypad::b_pressed() && sprites.size()>0){
            
            // Get a random index to erase
            int eraseIndex = r.get() % sprites.size();

            // Get the iterator for the matching item
            bn::vector<bn::sprite_ptr,1>::iterator it = sprites.begin() + eraseIndex;

            // Remove from the array
            // Since no other references exist, the sprite will be removed here
           sprites.erase(it);
        }

        bn::vector<bn::sprite_ptr,10>::iterator it = sprites.begin();

        while(it<sprites.end())     
        {
            // Get the x position of the sprite
            // Use -> to directly access the sprite data/functions, and not the optional data/functions
            bn::fixed x = it->x();

            x+=bn::fixed(2);

            if(x>160)sprites.erase(it);
            else{

                // Update the position of the sprite
                // Use -> to directly access the sprite data/functions, and not the optional data/functions
                it->set_x(x);

                it++;
            }

        }
        
    }
}

The result:

Conclusion

That’s the basics of sprites. In a later tutorial we’ll go over sprite animations, and maybe some other features. Be sure to check out the Butano sprites example to see everything the engine offers.

Leave a Reply

Your email address will not be published. Required fields are marked *

THANKS FOR READING!

If you have any suggestions, and/or are confused about anything: feel free to leave a comment, send me a email, or a message on social media. Constructive criticism will help the gameyard, and others like yourself. If you learned something from this tutorial, and are looking for more content, check other these other tutorials. 

Drawing sprites on the Game Boy Advance

Thanks for reading my tutorial, here are the files for that tutorial. 

Download Instructions

You’ll need the Butano Game Engine installed to run this. In addition, you’ll need the ability to run Makefiles. After you unzip the code, Make sure you update the LIBBUTANO . It should point to the “butano” folder INSIDE of your butano download folder. For me it was “c:/butano/butano”. After that, you can create your .gba file by running make.

If you want to make/request changes for the code, you can send me an email or put in a PR request on GitHub. Here’s a GitHub link for the code below.

Sign-Up for the "Gameyard Newsletter" for MOre Game Development News

If you like Retro Game Development, subscribe to the Gameyard Newsletter. Stay up-to-date with all the latest Game Boy news. It’s free, and you can unsubscribe any time!

Be sure to check your email inbox for a confirmation email.