Ah, finally time to program this.
Start by making a new source file: ball.c. Add it to your source directory.
Also, make a new directory in your project, call it include. The default makefile will add this directory to the list of folders containing files to be #include(d) in the source. Make a new file called ball.h and add it to the include directory.
We need to make a struct to define what variables the ball needs. Open up ball.h.
Now, just to be safe, you should always wrap the code in your header files to make sure that it's only included once in any source file. If the header file is included multiple times in a singe source file, errors will probably result.
Like this:
// ball.h #ifndef BALL_H #define BALL_H // insert code #endif |
Since BALL_H was defined inside the #ifndef (this preprocessor code is to include code ONLY if the condition is false), if the #ifndef happens again (if the source file includes the header twice somehow), then it will fail and protect your code from breaking.
Now, when I say "// insert code" I do NOT mean actual source code, source code belongs in source files only. Headers should only contain references to the source code and other definitions like structures. It may contain static source code though (usually inline, for small helper functions, etc).
Let's make a structure to hold the variables for the ball object.
#ifndef BALL_H
#define BALL_H
typedef struct t_ball
{
int x; // 24.8 fixed point
int y; // 24.8 fixed point
int xvel; // 20.12 fixed point
int yvel; // 24.8 fixed point
u8 sprite_index; // OAM entry (0->127)
u8 sprite_affine_index; // OAM affine entry (0->31)
int height; // height of ball
} ball;
void ballUpdate( ball* b );
void ballRender( ball* b, int camera_x, int camera_y );
#endif
|
This struct contains all the elements for our ball object. 'x' holds the horizontal position, measured in 24.8 fixed point, or 256 units per pixel. 'y' is the vertical position, in the same format.
xvel is the horizontal velocity. This will be added to the horizontal position every frame. yvel is the same, but for the vertical position. yvel is in the same 24.8 format, but xvel is in a higher precision 20.12 format.
sprite_index is the entry in the OAM/sprite this object will use for rendering itself. There are 128 entries, so this value ranges from 0->127.
sprite_affine selects an entry in the OAM affine data. There are 32 entries, so this value ranges from 0->31. This won't be used until next chapter.
height is the current height of the ball. This will be used in the 'squishing' chapter. :P
We also have two other definitions here, two references to the update functions [written in section below]. These functions will be called by main.c.
Open up ball.c now. Before we start writing code, we want to include 2 files. The libnds definitions, and our new ball reference.
#include <nds.h> #include "ball.h" |
We will have 2 functions, one for updating the ball physics, and one for rendering the ball. The update one should be called once per frame in the 'logic' section of the main loop. And the other should be called during VBlank.
void ballUpdate( ball* b )
{
}
void ballRender( ball* b, int camera_x, int camera_y )
{
}
|
Each function takes a pointer to the ball object to be updated/rendered, we can make multiple ball objects like this! ballRender takes two other parameters specifying the coordinates of the camera ...in pixels.
We will start with ballRender.
In our render function, we want to convert the position of the ball to pixels, and transform it with the camera position. With the new coordinates, we will modify an entry of the OAM to render the sprite.
Let's make a pointer to the sprite OAM entry.
void ballRender( ball* b, int camera_x, int camera_y )
{
u16* sprite = OAM + b->sprite_index * 4;
...
|
Each sprite entry is 4 Hwords wide. This gets the base address of the sprite entry to use.
Next, we'll translate the X/Y coordinates to sprite coordinates.
...
int x, y;
x = ((b->x - c_radius) >> 8) - camera_x;
y = ((b->y - c_radius) >> 8) - camera_y;
...
|
c_radius should be defined somewhere at the top of the source code, it holds the radius of the ball, which is 8. Since the coordinates are in 24.8 fixed point, radius needs to be in that format too.
#define c_radius (8<<8) |
The coordinates are then shifted into an integer value, and the camera offsets are subtracted.
Now, before we set the sprite entry, let's check if our values are actually in the screen boundaries. Although you can specify coordinats that are off the screen, if the coordiantes stretch too far our there, they will wrap around to the other side of the screen, not very desired!
...
if( x <= -16 || y <= -16 || x >= 256 || y >= 192 )
{
// sprite is out of bounds
// disable the sprite
sprite[0] = ATTR0_DISABLED;
return;
}
...
|
With the above code, if the sprite position is out of bounds, then it will disable the sprite target, and the function will exit.
We can set the sprite attributes now. We remember that attribute0 holds the Y position. We must also mask the value to 8-bits to truncate any sign-extension.
sprite[0] = y & 255;
|
The X coordinate is stored in attribute1, along with the size setting. We want to draw a 16x16 image. Don't forget to mask the X value to cut off the sign-extension.
sprite[1] = (x & 511) | ATTR1_SIZE_16;
|
The last attribute controls the tile entry, priority, and palette.
Now, although it's bad practice, I am feeling really really lazy. I'm just going to hardcode the tile and palette number to 0. (priority can be 0 too).
sprite[2] = 0;
|
The problem with this is that if you ever load the tiles somewhere else, you'll have to change this value too. You should always #define stuff that is to be used more than one time in the source code. It saves alot of headache when tweaking things.
This is enough code to produce some results. Let's go back to main.c.
Our main.c function is getting a little clutterd. Let's reorganize some of it. Start by replacing your main function with this.
//-----------------------------------------------------------
// program entry point
//-----------------------------------------------------------
int main( void )
//-----------------------------------------------------------
{
// setup things
setupInterrupts();
setupGraphics();
resetBall();
// start main loop
while( 1 )
{
// update game logic
processLogic();
// wait for new frame
swiWaitForVBlank();
// update graphics
updateGraphics();
}
return 0;
}
|
In the beginnin of the function, we setup things.
setupInterrupts is our new function to setup the interrupt handler and enable interrupts. We will move the irqInit/irqDisable combo in there.
//-----------------------------------------------------------
// setup interrupt handler with vblank irq enabled
//-----------------------------------------------------------
void setupInterrupts( void )
//-----------------------------------------------------------
{
// initialize interrupt handler
irqInit();
// enable vblank interrupt (required for swiWaitForVBlank!)
irqEnable( IRQ_VBLANK );
}
|
setupGraphics is the same function that we wrote in previous chapters. Oh, don't forget to remove the 'testing' code in setupGraphics. :P (the part that adds balls all over the screen)
resetBall is a new function to initialize our ball object. We will only have one ball object, so define a global variable to hold the ball attributes. Remember that the attributes [structure] are defined in ball.h, include that in main.c too.
#include "ball.h" ball g_ball; |
In resetBall, we will initialize our new object.
//-----------------------------------------------------------
// reset ball attributes
//-----------------------------------------------------------
void resetBall( void )
//-----------------------------------------------------------
{
// use sprite index 0 (0->127)
g_ball.sprite_index = 0;
// use affine matrix 0 (0->31)
g_ball.sprite_affine_index = 0;
// X = 128.0
g_ball.x = 128 << 8;
// Y = 64.0
g_ball.y = 64 << 8;
// start X velocity a bit to the right
g_ball.xvel = 100 << 4;
// reset Y velocity
g_ball.yvel = 0;
}
|
Now we have two more functions, processLogic and updateGraphics. The updateGraphics one will update graphical elements on the screen, so it must be called during the VBlank period to ensure a smooth display. updateLogic is where keypad input, ball physics, and other non graphic-related routines will go.
Right now, since we haven't programmed the ball physics yet, we'll just write the updateGraphics function. Just make processLogic an empty function to be filled in later.
//-----------------------------------------------------------
// update graphical information (call during vblank)
//-----------------------------------------------------------
void updateGraphics( void )
//-----------------------------------------------------------
{
// update ball sprite, camera = 0, 0
ballRender( &g_ball, 0, 0 );
}
|
The project can be compiled now, to see our premature output. :D
You should see the ball on the screen (where resetBall put it). We need to program the behavior now.

Before doing anything, open up main.c and add a ballUpdate call in processLogic. This is the update function that must be called every frame to update behavior.
void processLogic( void )
{
ballUpdate( &g_ball );
}
|
Open up ball.c again, and this time we'll write the contents of ballUpdate. This is the kind of fun part.
Start by writing a line to add the X velocity to the balls X position.
//-----------------------------------------------------------------
// update ball object (call once per frame)
//-----------------------------------------------------------------
void ballUpdate( ball* b )
//-----------------------------------------------------------------
{
// add X velocity to X position
b->x += (b->xvel>>4);
}
|
Notice the >>4 performed on xvel. This is because xvel is 20.12 fixed point format, while x is 24.8 format. xvel must be aligned to x's format before addition.
Compile and run the game, you should see the ball moving to the right. :D
Before we continue, let's define some constant values for the physics. I hand-tweaked these values until the ball's behavior felt somewhat right. :)
I also added a small function to clamp a value.
#define c_gravity 80 // gravity constant (add to vertical velocity) (*.8 fixed)
#define c_air_friction 1 // friction in the air... multiply X velocity by (256-f)/256
#define c_ground_friction 30 // friction when the ball hits the ground, multiply X by (256-f)/256
#define c_platform_level ((192-48) << 8) // the level of the brick platform in *.8 fixed point
#define c_bounce_damper 20 // the amount of Y velocity that is absorbed when you hit the ground
#define c_radius (8<<8) // the radius of the ball in *.8 fixed point
#define c_diam 16 // the diameter of the ball (integer)
#define min_height (1200) // the minimum height of the ball (when it gets squished) (*.8)
#define min_yvel (1200) // the minimum Y velocity (*.8)
#define max_xvel (1000<<4) // the maximum X velocity (*.12)
//-----------------------------------------------------------------
// clamp integer to range
//-----------------------------------------------------------------
static inline int clampint( int value, int low, int high )
//-----------------------------------------------------------------
{
if( value < low ) value = low;
if( value > high) value = high;
return value;
}
|
Let's apply the "air friction" to the X velocity. Also, let's clamp it to the maximum velocity setting.
// apply air friction to X velocity
b->xvel = (b->xvel * (256-c_air_friction)) >> 8;
// clamp X velocity to the limits
b->xvel = clampint( b->xvel, -max_xvel, max_xvel );
|
Compile and run. The ball should slowly stop in the air now.
Next we'll add the gravity constant to the Y velocity, and add the Y velocity to the Y position.
// add gravity to Y velocity
b->yvel += c_gravity;
// add Y velocity to Y position
b->y += (b->yvel);
|
Run the code, the ball should drop off the screen now! We'll make it bounce on the platform.
if( b->y + c_radius >= c_platform_level )
{
// apply ground friction to X velocity
b->xvel = (b->xvel * (256-c_ground_friction)) >> 8;
// mount Y on platform
b->y = c_platform_level - c_radius;
// negate Y velocity, also apply the bounce damper
b->yvel = -(b->yvel * (256-c_bounce_damper)) >> 8;
// clamp Y to mininum velocity (minimum after bouncing, so the ball does not settle)
if( b->yvel > -min_yvel )
b->yvel = -min_yvel;
}
|
First the code checks if Y + radius is greater than the brick platform level. If it is, it multiplies the X velocity by the ground friction, mounts the Y position on top of the platform, and reverses the Y velocity after multiplying by the damper.
Compile&run, the ball should be bouncing on the platform!! :D
This is pretty boring if we don't get to interact with the ball. Let's program some input routines to modify the ball's velocity with the directional pad. For GBA/DS reading the keypad is fairly easy. Most of it is done with the KEYINPUT register (this was the only register for GBA).

If the bit corresponding to the certain key is zero then the key is pressed (yes, zero). If the bit is 1 then the key is released. I can't express how easy using this register is compared to the methods used before GBA/DS were available. For SNES, you had to read the register at a certain number of clock cycles after the VBlank starts, I never could get the input right on that thing.
Anyway, if you haven't noticed, two of the keys are missing (3 actually). The X/Y buttons and the 'pen' button. These values are read on the ARM7 side.
libnds has some functions that can read the keypad input for us. The first one we'll use is scanKeys().
void processInput( void )
{
scanKeys();
...
|
The scanKeys function reads the KEYINPUT register into some internal variables. It also uses the previously read data to check if the key was just pressed, or just released.
To check if a key was just pressed, we can use the keysDown() function. It returns a bitmask of what keys were just clicked.
keysUp() returns a bitmask of what keys were just released.
keysHeld() returns a bitmask of what keys are held.
One more advantage of using the libnds keypad functions is that they retrieve the X, Y, and the PEN button from the ARM7 side too (a bit of a hassle saved).
We'll just use the keysHeld function for the keypad input. We'll make the directional keys modify the horizontal/vertical velocities of the ball.
int keysh = keysHeld();
// process user input
if( keysh & KEY_UP ) // check if UP is pressed
{
// tweak y velocity of ball
g_ball.yvel -= y_tweak;
}
if( keysh & KEY_DOWN ) // check if DOWN is pressed
{
// tweak y velocity of ball
g_ball.yvel += y_tweak;
}
if( keysh & KEY_LEFT ) // check if LEFT is pressed
{
// tweak x velocity
g_ball.xvel -= x_tweak;
}
if( keysh & KEY_RIGHT ) // check if RIGHT is pressed
{
// tweak y velocity
g_ball.xvel += x_tweak;
}
|
The KEY_??? symbols are libnds definitions to mask the keypad bitmask with.
Also, the x_tweak, and y_tweak values must be defined somewhere, it's how much will be added to the velocity when you press a key.
#define x_tweak (2<<8) // for user input #define y_tweak 25 // for user input |
Finally, add processInput() to your processLogic routine.
void processLogic( void )
{
processInput();
ballUpdate( &g_ball );
}
|
Compile and run the game, you should be able to control the ball's movement with the D-Pad now!! :D
"Whoops, the ball went out of the screen."
To fix this, we will add a 'camera' that will follow the ball around the screen. Make two more global variables.
int g_camera_x; int g_camera_y; |
These are the X and Y position of our camera. They will be in 24.8 fixed point format. We will need a function to update the camera.
void updateCamera( void )
{
// cx = desired camera X
int cx = ((g_ball.x)) - (128 << 8);
// dx = difference between desired and current position
int dx;
dx = cx - g_camera_x;
// 10 is the minimum threshold
if( dx > 10 || dx < -10 )
dx = (dx * 50) >> 10; // scale the value by some amount
// add the value to the camera X position
g_camera_x += dx;
// camera Y is always 0
g_camera_y = 0;
}
|
First this function calculates the position that the horizontal position of the camera desires, which is the ball in the center of the screen (ball - screen_width/2).
Second, this function gets the difference between the camera's X position and the desired position.
Third, we check if the distance is greater than a small threshold. This probably isn't needed, but it will ensure the camera stops exactly at the right position, instead of a little [unnoticably little :P] bit to the left/right.
Next we add the scaled difference to the camera's X position. This makes the camera trail a little bit behind the ball's movement.
The Y position of the camera will be set to zero.
Add updateCamera to your logic functions.
void processLogic( void )
{
processInput();
ballUpdate( &g_ball );
updateCamera();
}
|
Next, we'll modify updateGraphics to use the camera position.
//-----------------------------------------------------------
// update graphical information (call during vblank)
//-----------------------------------------------------------
void updateGraphics( void )
//-----------------------------------------------------------
{
// update ball sprite
ballRender( &g_ball, g_camera_x >> 8, g_camera_y >> 8 );
REG_BG0HOFS = g_camera_x >> 8;
}
|
We pass the camera position (the integer part/pixels) to ballRender. There's also something else that's needed, we need to modify the horizontal offset of the bricks BG, so we set the HOFS register with the camera position.
Compile and run the code... the camera should follow the ball now.
The next chapter will describe how to make the ball squishy. :D
| Previous: Fixed point arithmetic | Contents | Next: Squishing stuff |