Remar's Abstract Graphics Engine

News - About Rage - Screenshots - Downloads - Documentation - Tutorial - Contacting

Introduction

This tutorial will show you how to create a simple graphical demo using Rage. As Rage hides the details of the Nintendo DS graphics system, you should easily be able to get some graphics on the screen, without learning anything about tilemap offsets or VRAM mappings.

This tutorial will step by step implement a modified version of demo04 found in the Rage distribution, from creating the graphics and grit files to having a working .nds ready for testing. Have fun!

Assumptions

I will assume that you've already installed devkitARM and libnds, and that you're able to compile the examples. If that's not the case, you can have a look at this devkitPro page to get you started.

I will also assume that you know how to program in C and/or C++ as that will not be covered in this tutorial. If you've never programmed before, and desperatly want to learn how to make DS programs, there are a couple of good sources found in books and on the net. Some of these are:

Authors angle

I think it's important to tell you that I run Linux (or GNU/Linux if you're a Stallmanite (why yes, I am)), but hopefully the differences aren't that big between Linux and whatever you're running. Rage itself is developed in Linux, compiled in Linux, tested in Linux, and so on, so there might be initial issues when trying to use it in Windows, but I hope not. If any issues arise because of this, please contact me, I might actually fix it.

Installing Rage

Installing Rage should be pretty simple, follow these easy steps:

That last step will hopefully copy rage.h into libnds/include and librage.a into libnds/lib. If you don't want to copy anything into libnds (because you want it to be really clean) you can add the path to the Rage directory to the LIBDIRS line in the Makefile later on, instead of running 'make install' here.

Starting out

Ok, let's get started with setting up our project!

Copy template

First, copy the arm9 template from the libnds examples (you can find it in examples/templates/arm9). Name it something nice, like 'tutorial' or some other imaginative name.

Edit Makefile

Now, to be able to link this project against Rage, you will have to edit the Makefile. So open up 'tutorial/Makefile' in your favourite editor and add -lrage to the LIBS line. Make sure to add it before -lnds9. When you're done, the LIBS line will look something like this:

LIBS	:= 	-lrage -lnds9

Allrighty then!

If you skipped the 'make install' step earlier, you will also want to add the path to Rage to the LIBDIRS line, so it will look like this:

LIBDIRS	:=	$(LIBNDS) PATH_TO_RAGE

Renaming

To be able to link in Rage, which is implemented in C++, you must now rename main.c to main.cpp. I hope I don't have to show you how to perform a rename. 8-)

Please note that you won't have to use any features of C++ if you don't want to when using Rage, you can just use the C subset if that's enough for you. Ok, I know, it's not a real subset, but it's pretty close.

You can now try to compile this empty project by going into your 'tutorial' directory and typing 'make'. Hopefully you will get a tutorial.nds.

Graphics files

Ok, time to get some graphics into the project. The graphics format of Rage is 8bpp, i.e. each pixel is represented by 1 byte (8 bits). Each pixel value points into a palette that has 256 color values, each value being 16 bits (the color format is BGR555, if you wanna know, but don't worry, grit takes care of that for you). The first palette entry (index 0) is used as a "transparent" value, which means that pixels pointing to this entry won't be displayed. This can be used to create transparent parts in images.

Anyway, here's some graphics for you to use:

Create a 'gfx' directory in your 'tutorial' directory, and put the images there. The images are already in the correct graphics format (paletted) so you won't have to edit them. If you want to replace the graphics files with your own creations, make sure that they are indexed (paletted) 8bpp images with the correct dimensions (height and width divisible by 8).

Now your 'gfx' directory should look like this:

[andreas:~]$ ls tutorial/gfx
goodleft.png  goodright.png  metroid.png

Grit files

To tell grit (the GBA Raster Image Transmogrifier) how to convert the .png files into objects that can be linked into the code, you will have to create a grit file for each image. Each grit file consists of a list of arguments to the grit command that is automatically run when you type 'make' in the 'tutorial' directory.

So, create the file tutorial/gfx/goodleft.grit and open it in your $EDITOR. Here's what the grit file should look like:

[andreas:gfx]$ cat tutorial/gfx/goodleft.grit 
# Include palette
-p

# Include gfx data
-g

# Gfx data type
-gu16

# 8bpp
-gB8

# Exclude map data
-m!

# Metatile height (tiles are 16x16 pixels)
-Mh2

# Metatile width (tiles are 16x16 pixels)
-Mw2

This grit file includes alot of options that really are the default (e.g. -p, -g, -m!), but I include them anyway to be explicit. The most interesting thing to note is the metatile switches. This tells grit to not just linearly extract 8x8 pixel tiles from goodleft.png but instead group them in 16x16 pixel tiles. I'll show you with an example.

If you don't use metatiling, the tiles will be indexed like this:

But if you metatile with -Mh2 and -Mw2 (16x16 tiles), the tiles will be indexed like this instead:

So send lots of love to the maker(s) of grit for sparing you the awful work of doing this yourself. 8-)

Now, to complete this part, copy goodleft.grit to goodright.grit and metroid.grit. That's right, they all use the same format. So now your 'gfx' dir will contain this:

[andreas:~]$ ls tutorial/gfx/
goodleft.grit  goodright.grit  metroid.grit
goodleft.png   goodright.png   metroid.png

As you might have noticed, the .grit files are named the same as the .png files. I think you get the hint.

Entering code

Finally it's time to start coding!

Initial main.cpp

The default main.c file found in the arm9 template is not really a clean template, so replace the contents with this:
#include <nds.h>

int main(void)
{

  return 0;
}

It might be a good idea to actually make this change in the arm9 template as well, so that copying the template will start you out with a clean main.c.

Adding Rage

To use Rage, you have to include the Rage definitions found in rage.h. Add this after the inclusion of nds.h:

#include "rage.h"

You will also have to create a Rage object that will work as the interface to the functions in Rage. Add this after the previous line:

Rage rage;

This will create a Rage object in the global environment. If you don't want it to be global, there's nothing stopping you from placing it somewhere else, as long as you know what you're doing.

Initializing Rage

Now we're going to enter code in the main() function. Add this to the top of main:

rage.init()

This will set up a standard video mode on the DS, and also set up the standard VRAM mappings. (If you don't know what VRAM mappings are, you don't have to learn anything about it, don't worry.)

Setting up a background

Before you can start displaying the background you will have to set up an abstract tile map. What I mean by abstract is that the tiles in the map can have other dimensions than 8x8 pixels, e.g. 16x16 or 24x8. To set up a 16x16 pixel tile map in layer 0 on the main screen, type in:

rage.setupBackground(Rage::MAIN, 0, Rage::BG_MAP_256x256, 16, 16);

When you set up a tile map, the width and height of tiles must be divisible by 8, e.g. 8, 16, 24, 32 and so on. The Rage::BG_MAP_256x256 is the size of the map in pixels, so you will get a map that is 256 by 256 pixels. Height and width is either 256 or 512, so valid values are 256x256, 512x256, 256x512, and 512x512.

Loading background graphics

Finally, actually loading some graphics into the VRAM of the DS. To be able to load background graphics you will have to define a tile set. You do this by filling in a simple data structure, that looks like this in rage.h:

struct TileSetDefinition
  {
    int version;
    int tileSetID; 
    ImageDefinition image;
  };

The version attribute should be set to the current API version of Rage, 2 for now. The tileSetID attribute should be set to an integer between 0 and 15 (inclusive). Yes, this means that, at the moment, you can only load in 16 different tile sets per screen, this might change in the future. You will use this tileSetID later on to reference this tile set when setting tiles.

The image attribute is an ImageDefinition, which looks like this:

struct ImageDefinition
  {
    const u16 *gfx;
    u16 gfxLen;
    const u16 *pal;
    u16 palLen;
  };

If you've used grit before, you will see that this matches what grit outputs in the .h file when it's done. So, fill this in with what you find in the .h file created by grit. 8-)

Ok, I'll show you how to create this tile set definition. I like to keep the main.cpp source file clean, so I'm going to put this in a separate file and then include it. I'll call it metroiddef.h as it defines a tile set definition called metroidDef. This is what metroiddef.h contains:

#include "metroid.h" // generated by grit from gfx/metroid.png

#define METROID_TILESET 2

Rage::TileSetDefinition metroidDef;
metroidDef.version = 2;
metroidDef.tileSetID = METROID_TILESET;
metroidDef.image.gfx = metroidTiles;
metroidDef.image.gfxLen = metroidTilesLen;
metroidDef.image.pal = metroidPal;
metroidDef.image.palLen = metroidPalLen;

Notice that I make a #define METROID_TILESET that I then set the tileSetID to. I will then later on use this METROID_TILESET in the code when setting tiles. This is a good way to avoid 'magic numbers'.

Now, to actually load this tile set into Rage, add this to main:

#include "metroiddef.h"
rage.loadTileSet(Rage::MAIN, &metroidDef);

I know, you might be thinking 'WTF? You shouldn't have include files in your functions!', and I agree... but this is how I'm doing it right now, OK? If and when I learn how to use a file system on the DS, I'll create a nice file format for tile set definitions.

Setting tiles

Phew, still with me after the grunt work of defining and loading a tile set? Good! Now let's draw a nice background with our tile set! I'm going to use a method in Rage that sets all the tiles in one go, so first declare a u16 array of the correct dimension:

u16 map[16*16];

Why 16*16? Because when using 16x16 pixel tiles, you will fit 16 tiles in a row and 16 tiles in a column. So there. To calculate the map size, you can use this simple formula: roof(mapWidth/tileWidth)*roof(mapHeight/tileHeight). If your tiles doesn't fit exactly on the screen, they will be clipped. E.g. if you use 24x24 pixel tiles, one row will contain 10 and 2/3 of a tile, so the row will be 11 tiles wide. I hope this makes sense.

Let's fill the map with tiles:

for(int i = 0;i < 16*16;i++)
  {
    if(i/16 == 5)
      map[i] = 0;
    else if(i/16 == 6)
      map[i] = rand() % 3 + 1;
    else
      map[i] = rand() % 4;
  }

The different cases simply means that row 6 (index 5) should be empty and row 7 (index 6) should definitely not be empty. All other tiles will be random.

Now, to display this elegant tile map, add this to your main function:

rage.setMap(Rage::MAIN, 0, METROID_TILESET, map);

Notice that I use the METROID_TILESET that I defined in the metroiddef.h earlier.

The main loop

As always when making a game, we need a main loop. Our main loop will be very simple right now as we're simply displaying a background. It looks like this:

while(1)
  {
    rage.redraw();
  }

This will loop forever and update the screen at a rate of 60 frames per second.

Ok, let's take a break here and try to compile this (it's always good to compile now and then to catch the simple mistakes that always creeps into code). If the compilation succeeds, you will now have a tutorial.nds that actually does something! If you're not able to compile, here's how my main.cpp looks like at this stage:

#include <nds.h>
#include "rage.h"

Rage rage;

int main(void)
{
  rage.init();

  rage.setupBackground(Rage::MAIN, 0, Rage::BG_MAP_256x256, 16, 16);

#include "metroiddef.h"
  rage.loadTileSet(Rage::MAIN, &metroidDef);

  u16 map[16*16];

  for(int i = 0;i < 16*16;i++)
    {
      if(i/16 == 5)
	map[i] = 0;
      else if(i/16 == 6)
	map[i] = rand() % 3 + 1;
      else
	map[i] = rand() % 4;
    }

  rage.setMap(Rage::MAIN, 0, METROID_TILESET, map);
 
  while(1)
    {
      rage.redraw();
    }

  return 0;
}

Loading a sprite

Just as with tile sets, you will have to create a sprite definition to load in a sprite. The sprite definition struct looks like this in rage.h:

struct SpriteDefinition
{
  int version;
  u16 spriteID;
  u16 animationCount;
  Animation *animations;
};

The version attribute should be set to the current API version of Rage, right now this is 2. The spriteID attribute should be a number between 0 and 127 (inclusive). You will use this spriteID later on when creating sprite instances of this sprite definition. The animationCount attribute is simply the number of animations this sprite contains.

The animations attribute is a pointer to an array of type Animation. The Animation struct looks like this:

struct Animation
{
  ImageDefinition image;
  SpriteSize size;
  bool looping;
  u16 frameCount;
  Frame *frames;
};

Each animation has an image associated with it, fill in the image attribute with the names found in the .h file produced by grit.

The size attribute indicates the size of each frame of animation, and uses the SpriteSize enum found in libnds. The valid values are: SpriteSize_8x8, SpriteSize_16x16, SpriteSize_32x32, SpriteSize_64x64, SpriteSize_16x8, SpriteSize_32x8, SpriteSize_32x16, SpriteSize_64x32, SpriteSize_8x16, SpriteSize_8x32, SpriteSize_16x32, and SpriteSize_32x64.

The looping attribute is used to indicate if this animation should loop or not. Quite obvious, right?

The frameCount should be set to the number of frames in this animation, and the frames attribute is a pointer to an array of type Frame. Frame is defined like this:

struct Frame
{
  int index;
  int duration;
};

The index attribute is an index into the animation image, and the duration attribute is the number of screen updates that this frame should be visible. So if you set duration to 3, this frame will be visible for 3 screen updates (1/20th of a second). A useful feature when defining a frame is to set the index to -1, this will indicate that the frame is 'empty', which means that the sprite will be hidden during the duration of the frame. This can be useful if you want blinking sprites or to hide a sprite when its animation has played.

Mmmmkay, now we have enough information to create our first sprite definition. It will be a Captain Good sprite with two animations, one for walking left and one for walking right. The sprite definition found in demo04 actually contains animations for standing still (left and right) and jumping (left and right), but I feel like making the sprite definition a little shorter for this tutorial. Have a look in 'rage-0.2.0/examples/demo04/source/cpngooddef.h' if you want to see a sprite definition with six animations.

Ok, let's get crackin'! As with the tile set definition, I'm going to put the sprite definition in a separate file, this time called 'cpngooddef.h'. So create this file in the 'source' directory so we can get started.

Let's start with including the files produced by grit:

#include "goodleft.h"
#include "goodright.h"

Next let's add some definitions and enums that will come in handy:

#define GOOD_SPRITE 0
enum {WALK_LEFT, WALK_RIGHT};

These constants will be used in this file, and in main.cpp as well.

Now, you can create the various parts of the sprite definition in different orders, first all the frame definitions, or first the sprite definition and fill it in further down, and so on. I'll go with the latter. So let's declare the SpriteDefinition and fill in the basics:

Rage::SpriteDefinition cpngoodDef;
cpngoodDef.version = 2;
cpngoodDef.spriteID = GOOD_SPRITE;
cpngoodDef.animationCount = 2;

Then add the array of animations:

Rage::Animation cpngoodAnimations[2];
cpngoodDef.animations = cpngoodAnimations;

Now we're ready to define the first animation, Captain Good walking left. It will consist of 4 frames, and index 1 of the image is used twice. First let's declare the image for the walking left animation:

Rage::ImageDefinition goodleftImage;
goodleftImage.gfx = goodleftTiles;
goodleftImage.gfxLen = goodleftTilesLen;
goodleftImage.pal = goodleftPal;
goodleftImage.palLen = goodleftPalLen;

And then the array of frames for this animation:

Rage::Frame walkleftFrames[4];
walkleftFrames[0].index = 0; walkleftFrames[0].duration = 5;
walkleftFrames[1].index = 1; walkleftFrames[1].duration = 5;
walkleftFrames[2].index = 2; walkleftFrames[2].duration = 5;
walkleftFrames[3].index = 1; walkleftFrames[3].duration = 5;

Let's fill in index 0 (WALK_LEFT) of the animations array with the animation details:

cpngoodAnimations[WALK_LEFT].image = goodleftImage;
cpngoodAnimations[WALK_LEFT].size = SpriteSize_16x16;
cpngoodAnimations[WALK_LEFT].looping = true;
cpngoodAnimations[WALK_LEFT].frameCount = 4;
cpngoodAnimations[WALK_LEFT].frames = walkleftFrames;

So, that's the first animation!

The walk right animation is almost identical, and I could actually have reused the walkleftFrames array, but I choose not to to be more complete:

Rage::ImageDefinition goodrightImage;
goodrightImage.gfx = goodrightTiles;
goodrightImage.gfxLen = goodrightTilesLen;
goodrightImage.pal = goodrightPal;
goodrightImage.palLen = goodrightPalLen;

Rage::Frame walkrightFrames[4];
walkrightFrames[0].index = 0; walkrightFrames[0].duration = 5;
walkrightFrames[1].index = 1; walkrightFrames[1].duration = 5;
walkrightFrames[2].index = 2; walkrightFrames[2].duration = 5;
walkrightFrames[3].index = 1; walkrightFrames[3].duration = 5;

cpngoodAnimations[WALK_RIGHT].image = goodrightImage;
cpngoodAnimations[WALK_RIGHT].size = SpriteSize_16x16;
cpngoodAnimations[WALK_RIGHT].looping = true;
cpngoodAnimations[WALK_RIGHT].frameCount = 4;
cpngoodAnimations[WALK_RIGHT].frames = walkrightFrames;

Pheeew, that's alot of code. But when you're making your own sprite definitions, you can copy-paste alot of the lines and just edit a little. For example, when assigning values to the frame array, the lines are almost identical.

Back to main.cpp! To load the defined sprite into Rage, add the following code below the call to setMap:

#include "cpngooddef.h"
rage.loadSprite(Rage::MAIN, &cpngoodDef);

The Rage::MAIN points out the screen to load the sprite definition into. As for tile sets, this can be either Rage::MAIN or Rage::SUB.

Before you can actually display Captain Good on the screen you will have to create an instance of the sprite. This is done with a call to createSpriteInstance:

int good = rage.createSpriteInstance(Rage::MAIN, GOOD_SPRITE);

Note that the return value is stored in the 'good' integer. This variable is used later on when refering to this sprite instance. Let's use this immediately! This is how you set the animation for the sprite instance:

rage.selectAnimation(Rage::MAIN, good, WALK_RIGHT);

If you don't select animation, it will simply start the first animation defined in the sprite. Technical note: nothing is loaded into VRAM before an animation is started, and only the image associated with the current animations playing are loaded into VRAM.

Let's make this demo a little interesting by making Captain Good walk back and forth over the screen. To keep track of his position and direction, define these variables:

int x = 0;
int dx = 1;

Next we'll add some code to move Captain Good one pixel each frame. He'll move in the direction of dx, simply by adding dx to x. Each frame we also check if we've reached the edge of the screen, in that case Captain Good will change direction. This is what the main loop looks like after these additions:

while(1)
  {
    x += dx;

    rage.moveSpriteAbs(Rage::MAIN, good, x, 5*16);

    if(x == 0 || x == 255 - 16)
      {
	dx = -dx;
	rage.selectAnimation(Rage::MAIN, good, dx==1 ? WALK_RIGHT : WALK_LEFT);
      }

    rage.redraw();
  }

Compiling

Oh yeah, let's compile! Type 'make' in the 'tutorial' directory and cross your fingers (or hold your thumbs if you're in Sweden). If all goes well, the final demo will appear in your 'tutorial' dir, and look something like this when running:

If all didn't go well when compiling, here's the complete main.cpp after the addition of the sprite stuff, and also listings of the metroiddef.h and cpngooddef.h files for completeness:

main.cpp

#include <nds.h>
#include "rage.h"

Rage rage;

int main(void)
{
  rage.init();

  rage.setupBackground(Rage::MAIN, 0, Rage::BG_MAP_256x256, 16, 16);

#include "metroiddef.h"
  rage.loadTileSet(Rage::MAIN, &metroidDef);

  u16 map[16*16];

  for(int i = 0;i < 16*16;i++)
    {
      if(i/16 == 5)
	map[i] = 0;
      else if(i/16 == 6)
	map[i] = rand() % 3 + 1;
      else
	map[i] = rand() % 4;
    }

  rage.setMap(Rage::MAIN, 0, METROID_TILESET, map);

#include "cpngooddef.h"
  rage.loadSprite(Rage::MAIN, &cpngoodDef);

  int good = rage.createSpriteInstance(Rage::MAIN, GOOD_SPRITE);
 
  rage.selectAnimation(Rage::MAIN, good, WALK_RIGHT);

  int x = 0;
  int dx = 1;

  while(1)
    {
      x += dx;

      rage.moveSpriteAbs(Rage::MAIN, good, x, 5*16);

      if(x == 0 || x == 255 - 16)
	{
	  dx = -dx;
	  rage.selectAnimation(Rage::MAIN, good, dx==1 ? WALK_RIGHT : WALK_LEFT);
	}

      rage.redraw();
    }

  return 0;
}

metroiddef.h

#include "metroid.h" // generated by grit from gfx/metroid.png

#define METROID_TILESET 2

Rage::TileSetDefinition metroidDef;
metroidDef.version = 2;
metroidDef.tileSetID = METROID_TILESET;
metroidDef.image.gfx = metroidTiles;
metroidDef.image.gfxLen = metroidTilesLen;
metroidDef.image.pal = metroidPal;
metroidDef.image.palLen = metroidPalLen;

cpngooddef.h

#include "goodleft.h"
#include "goodright.h"

#define GOOD_SPRITE 0
enum {WALK_LEFT, WALK_RIGHT};

Rage::SpriteDefinition cpngoodDef;
cpngoodDef.version = 2;
cpngoodDef.spriteID = GOOD_SPRITE;
cpngoodDef.animationCount = 2;

Rage::Animation cpngoodAnimations[2];
cpngoodDef.animations = cpngoodAnimations;


Rage::ImageDefinition goodleftImage;
goodleftImage.gfx = goodleftTiles;
goodleftImage.gfxLen = goodleftTilesLen;
goodleftImage.pal = goodleftPal;
goodleftImage.palLen = goodleftPalLen;

Rage::Frame walkleftFrames[4];
walkleftFrames[0].index = 0; walkleftFrames[0].duration = 5;
walkleftFrames[1].index = 1; walkleftFrames[1].duration = 5;
walkleftFrames[2].index = 2; walkleftFrames[2].duration = 5;
walkleftFrames[3].index = 1; walkleftFrames[3].duration = 5;

cpngoodAnimations[WALK_LEFT].image = goodleftImage;
cpngoodAnimations[WALK_LEFT].size = SpriteSize_16x16;
cpngoodAnimations[WALK_LEFT].looping = true;
cpngoodAnimations[WALK_LEFT].frameCount = 4;
cpngoodAnimations[WALK_LEFT].frames = walkleftFrames;


Rage::ImageDefinition goodrightImage;
goodrightImage.gfx = goodrightTiles;
goodrightImage.gfxLen = goodrightTilesLen;
goodrightImage.pal = goodrightPal;
goodrightImage.palLen = goodrightPalLen;

Rage::Frame walkrightFrames[4];
walkrightFrames[0].index = 0; walkrightFrames[0].duration = 5;
walkrightFrames[1].index = 1; walkrightFrames[1].duration = 5;
walkrightFrames[2].index = 2; walkrightFrames[2].duration = 5;
walkrightFrames[3].index = 1; walkrightFrames[3].duration = 5;

cpngoodAnimations[WALK_RIGHT].image = goodrightImage;
cpngoodAnimations[WALK_RIGHT].size = SpriteSize_16x16;
cpngoodAnimations[WALK_RIGHT].looping = true;
cpngoodAnimations[WALK_RIGHT].frameCount = 4;
cpngoodAnimations[WALK_RIGHT].frames = walkrightFrames;

A second view

To show you how to do scrolling in Rage, I'll add a second view to the demo, displayed on the sub screen. I think it's pretty straight forward, much of it will be to just copy and paste code you've already added.

So here goes. First, set up the background on the sub screen as well, place this after the other call to setupBackground:

rage.setupBackground(Rage::SUB, 0, Rage::BG_MAP_256x256, 16, 16);

As you can see, this line is identical to the previous call to setupBackground, except that we use Rage::SUB instead of Rage::MAIN.

Here's another copy-n-paste with only a change of screen:

rage.loadTileSet(Rage::SUB, &metroidDef);

I guess you can figure out where to place it.

I'll give you a couple more lines that look almost identical, let's see if you can fit them into main.cpp:

rage.setMap(Rage::SUB, 0, METROID_TILESET, map);
rage.loadSprite(Rage::SUB, &cpngoodDef);
int goodSub = rage.createSpriteInstance(Rage::SUB, GOOD_SPRITE);
rage.selectAnimation(Rage::SUB, goodSub, WALK_RIGHT);

Good job! (I assume you've been able to fit the pieces into main.cpp.) After the last selectAnimation call, add this little bit of code:

rage.moveSpriteAbs(Rage::SUB, goodSub, 120, 5*16);

Yep, we're gonna let the second Captain Good sprite just sit in the middle of the screen. It'll still look like he's running around, but it will be the background that is scrolling instead.

To make this second Captain Good change direction at the appropriate time, add this bit of code where it seems to fit:

rage.selectAnimation(Rage::SUB, goodSub, dx==1 ? WALK_RIGHT : WALK_LEFT);

And finally, before the call to rage.redraw(), add this line of code:

rage.setBackgroundScroll(Rage::SUB, 0, x-120, 0);

And recompile! Hopefully you will have two views of Cpn. Good running around now, on the top screen it's the sprite moving, on the bottom screen it's the background moving.

I'll show you the complete main.cpp here as well, if you didn't manage to puzzle in the pieces I provided:

main.cpp with scrolling

#include <nds.h>
#include "rage.h"

Rage rage;

int main(void)
{
  rage.init();

  rage.setupBackground(Rage::MAIN, 0, Rage::BG_MAP_256x256, 16, 16);
  rage.setupBackground(Rage::SUB, 0, Rage::BG_MAP_256x256, 16, 16);

#include "metroiddef.h"
  rage.loadTileSet(Rage::MAIN, &metroidDef);
  rage.loadTileSet(Rage::SUB, &metroidDef);

  u16 map[16*16];

  for(int i = 0;i < 16*16;i++)
    {
      if(i/16 == 5)
	map[i] = 0;
      else if(i/16 == 6)
	map[i] = rand() % 3 + 1;
      else
	map[i] = rand() % 4;
    }

  rage.setMap(Rage::MAIN, 0, METROID_TILESET, map);
  rage.setMap(Rage::SUB, 0, METROID_TILESET, map);

#include "cpngooddef.h"
  rage.loadSprite(Rage::MAIN, &cpngoodDef);
  rage.loadSprite(Rage::SUB, &cpngoodDef);

  int good = rage.createSpriteInstance(Rage::MAIN, GOOD_SPRITE);
  int goodSub = rage.createSpriteInstance(Rage::SUB, GOOD_SPRITE);
 
  rage.selectAnimation(Rage::MAIN, good, WALK_RIGHT);
  rage.selectAnimation(Rage::SUB, goodSub, WALK_RIGHT);

  rage.moveSpriteAbs(Rage::SUB, goodSub, 120, 5*16);

  int x = 0;
  int dx = 1;

  while(1)
    {
      x += dx;

      rage.moveSpriteAbs(Rage::MAIN, good, x, 5*16);

      if(x == 0 || x == 255 - 16)
	{
	  dx = -dx;
	  rage.selectAnimation(Rage::MAIN, good, dx==1 ? WALK_RIGHT : WALK_LEFT);
	  rage.selectAnimation(Rage::SUB, goodSub, dx==1 ? WALK_RIGHT : WALK_LEFT);
	}

      rage.setBackgroundScroll(Rage::SUB, 0, x-120, 0);

      rage.redraw();
    }

  return 0;
}

Error checking

Something I haven't covered so far in the tutorial is error checking, but in a real program you should make sure to check the return values of all calls to Rage. In general, if a method returns 0, the call failed for some reason. To see why it failed, you can call getErrorCode, or getErrorString to get the error as a descriptive string (char array). There's a RAGE_FAILED macro defined in rage.h that can be used for error checking. Use it like this:

if(RAGE_FAILED(rage.init()))
{
  // error handling
  switch(rage.getErrorCode())
  {
    ...
  }
}

In demo01 there's a macro defined that makes a check of return value and calls an error handling function when a call fails. Here's the code of the macro and how it's used:

#define TRY(x) {if(RAGE_FAILED(x)) displayError();}
TRY(rage.init());

The displayError function simply turns on the standard console in libnds, displays the error message from Rage, and locks up in an infinite loop. Not a very pretty error handling method, but useful when developing.

Keeping track of memory usage

If you're having problems with running out of VRAM or sprite instances, there are a couple of methods you can use. To get the amount of free VRAM (in bytes) for either backgrounds or sprites, on either the main or the sub screen, use getFreeMem. Like this:

int freeMem = rage.getFreeMem(Rage::MAIN, Rage::BG);

This will hopefully be useful while developing. If fragmentation is an issue (loading and unloading a lot, in weird order) you can use getLargestFreeBlock to get the largest free consecutive block of bytes in a VRAM bank:

int freeMem = rage.getLargestFreeBlock(Rage::SUB, Rage::SPRITE);

To see how many more sprite instances you can create on a screen, use getAvailableSprites:

int spritesAvailable = rage.getAvailableSprites(Rage::MAIN);

Wrapping up

I hope you've been able to follow along with this tutorial, and hopefully that you see how you can use Rage to create cool games. If you find any errors in this tutorial, or in Rage in general, be sure to tell me about it.

You can read the documentation for Rage here, and get a listing of rage.h here.

One last thing. Who's Captain Good? Oh, it's the main character from Retrobattle created by Daniel Remar. I've "borrowed" the sprite. 8-)

Peace out,
Andreas Remar