Jul 21

NDS Dev – Simple sprite movement

Another adaptation of one of Libnds example code projects. You can use the keypad or the touchscreen to move around, use A to talk, B to clear the text.

 
#include <nds.h>
#include <stdio.h>
#include <man.h>
 
#define FRAMES_PER_ANIMATION 3
 
typedef struct 
{
	int x;
	int y;
 
	u16* sprite_gfx_mem;
	u8*  frame_gfx;
 
	int state;
	int anim_frame;
}Man;
 
enum SpriteState {W_UP = 0, W_RIGHT = 1, W_DOWN = 2, W_LEFT = 3};
enum {SCREEN_TOP = 0, SCREEN_BOTTOM = 192, SCREEN_LEFT = 0, SCREEN_RIGHT = 256};
 
touchPosition touch;
 
void animateMan(Man *sprite)
{
	int frame = sprite->anim_frame + sprite->state * FRAMES_PER_ANIMATION;
	u8* offset = sprite->frame_gfx + frame * 32*32;
	dmaCopy(offset, sprite->sprite_gfx_mem, 32*32);
}
 
void initMan(Man *sprite, u8* gfx)
{
	sprite->sprite_gfx_mem = oamAllocateGfx(&oamMain, SpriteSize_32x32, SpriteColorFormat_256Color);
	sprite->frame_gfx = (u8*)gfx;
}
 
void printStaticText()
{
	static int i = 0;
	static int j = 0;
	char text[] = {"Hi, I'm Ninten\n"};
 
	while(text[i]!= '\0')
	{
		if(j++ > 99999)
		{
			iprintf("%c", text[i++]);
			j = 0;
		}
	}
	i = 0;
}
 
int main(void) 
{
	Man man = {(SCREEN_WIDTH-16) / 2, (SCREEN_HEIGHT-16) / 2};
	man.state = W_DOWN;
	int count = 0;
 
	//-----------------------------------------------------------------
	// Initialize the graphics engines
	//-----------------------------------------------------------------
	videoSetMode(MODE_0_2D);
	vramSetBankA(VRAM_A_MAIN_SPRITE);
	oamInit(&oamMain, SpriteMapping_1D_128, false);
 
	initMan(&man, (u8*)manTiles);
	dmaCopy(manPal, SPRITE_PALETTE, 512);
 
	consoleDemoInit();
 
	while(1) 
	{
		scanKeys();
		touchRead(&touch);
 
		int yPos = 0;
		int xPos = 0;
		int keys = keysHeld();
 
 
 
		if(keys)
		{
			if(keys & KEY_UP)
			{
				if(man.y >= SCREEN_TOP) 
					man.y--;
				man.state = W_UP;
			}
			if(keys & KEY_LEFT)
			{
				if(man.x >= SCREEN_LEFT) 
					man.x--;
				man.state = W_LEFT;
			}
			if(keys & KEY_RIGHT)
			{
				if(man.x <= SCREEN_RIGHT - 32) 
					man.x++;
				man.state = W_RIGHT;
			}
			if(keys & KEY_DOWN)
			{
				if(man.y <= SCREEN_BOTTOM - 32)
					man.y++;
				man.state = W_DOWN;
			}
			if(touch.px > 0)
			{
				xPos = touch.px;
				if(xPos > man.x)
				{
					if(man.x <= SCREEN_RIGHT - 32) 
						man.x++;
					man.state = W_RIGHT;
				}
				if(xPos < man.x)
				{
					if(man.x >= SCREEN_LEFT) 
						man.x--;
					man.state = W_LEFT;
				}
			}
			if(touch.py > 0)
			{
				yPos = touch.py;
				if(yPos > man.y)
				{
					if(man.y <= SCREEN_BOTTOM - 32) 
						man.y++;
					man.state = W_DOWN;
				}
				if(yPos < man.y)
				{
					if(man.y >= SCREEN_TOP) 
						man.y--;
					man.state = W_UP;
				}
			}
			if(count++ > 10 && (man.x != xPos || man.y != yPos))
			{
				man.anim_frame++;
				count = 0;
			}
 
			if(man.anim_frame >= FRAMES_PER_ANIMATION) man.anim_frame = 0;
		}
		animateMan(&man);
 
		oamSet(&oamMain, 0, man.x, man.y, 0, 0, SpriteSize_32x32, SpriteColorFormat_256Color, 
			man.sprite_gfx_mem, -1, false, false, false, false, false);
 
		if(keys & KEY_A)
			printStaticText();	
		if(keys & KEY_B)
			consoleDemoInit();
 
		swiWaitForVBlank();
 
		oamUpdate(&oamMain);
	}
	return 0;
}

The code in action:

Jul 18

DS Programming – Simple Starfield

The third thing I tried was an adaptation of Dev-Scene’s Starfield. It took me a while to figure out how to add multiple button checking, not sure if my solution is ‘the way to go’ but at least it works.

With the following code you generate a starfield and can change its movement in all eight directions.

#include <nds.h>
#include <stdlib.h>
 
#define NUM_STARS 40
#define UPLEFT (KEY_UP | KEY_LEFT)
#define UPRIGHT (KEY_UP | KEY_RIGHT)
#define DOWNLEFT (KEY_DOWN | KEY_LEFT)
#define DOWNRIGHT (KEY_DOWN | KEY_RIGHT)
 
typedef struct 
{
	int x;
	int y;
	int speed;
	unsigned short color;
 
}Star;
 
// Function Prototypes
void setStar(Star* star, int xSpeed, int ySpeed, int xStart, int yStart);
void MoveStar(Star* star);
void ClearScreen(void);
void InitStars(void);
void DrawStar(Star* star);
void EraseStar(Star* star);
 
Star stars[NUM_STARS];
int lastKey;
 
void MoveStar(Star* star)
{
	if((DOWNRIGHT & lastKey) == DOWNRIGHT)
		setStar(star, star->speed, star->speed, rand() % SCREEN_WIDTH, 0);
	else if((DOWNLEFT & lastKey) == DOWNLEFT)
		setStar(star, -star->speed, +star->speed, rand() % SCREEN_WIDTH, 0);
	else if((UPLEFT & lastKey) == UPLEFT)
		setStar(star, -star->speed, -star->speed, rand() % SCREEN_WIDTH, SCREEN_HEIGHT);
	else if((UPRIGHT & lastKey) == UPRIGHT)
		setStar(star, star->speed, -star->speed, rand() % SCREEN_WIDTH, SCREEN_HEIGHT);
	else if(KEY_RIGHT & lastKey)
		setStar(star, star->speed, 0, 0, rand() % SCREEN_HEIGHT);
	else if(KEY_LEFT & lastKey)
		setStar(star, -star->speed, 0, SCREEN_WIDTH, rand() % SCREEN_HEIGHT);
	else if(KEY_UP & lastKey)
		setStar(star, 0, -star->speed, rand() % SCREEN_WIDTH, SCREEN_HEIGHT);
	else if(KEY_DOWN & lastKey)
		setStar(star, 0, star->speed, rand() % SCREEN_WIDTH, 0);
	else
		setStar(star, star->speed, 0, 0, rand() % SCREEN_HEIGHT);
}
 
void setStar(Star* star, int xSpeed, int ySpeed, int xStart, int yStart)
{
	star->x += xSpeed;
	star->y += ySpeed;
 
	if(star->y < 0 || star->y > SCREEN_HEIGHT)
	{
		star->x = xStart;
		star->y = yStart;
		star->speed = rand() % 4 + 1;	
	}
	else if(star->x < 0 || star->x > SCREEN_WIDTH)
	{
		star->x = xStart;
		star->y = yStart;
		star->speed = rand() % 4 + 1;	
	}
}
 
void ClearScreen(void)
{
     int i;    
     for(i = 0; i < 256 * 192; i++)
           VRAM_A[i] = RGB15(0,0,0);
}
 
void InitStars(void)
{
	int i;
	for(i = 0; i < NUM_STARS; i++)
	{
		stars[i].color = rand(); // both color & rand() are a 16bit value
		stars[i].x = rand() % 256;
		stars[i].y = rand() % 192;
		stars[i].speed = rand() % 4 + 1;
	}
}
void DrawStar(Star* star)
{
	VRAM_A[star->x + star->y * SCREEN_WIDTH] = star->color;
}
 
void EraseStar(Star* star)
{
	VRAM_A[star->x + star->y * SCREEN_WIDTH] = RGB15(0,0,0);
}
 
int main(void) 
{
	int i;
 
	irqInit();
	irqEnable(IRQ_VBLANK);
 
	videoSetMode(MODE_FB0);
	vramSetBankA(VRAM_A_LCD);
 
    ClearScreen();
	InitStars();
 
	while(1)
	{
		scanKeys();
		if(keysDown())
			lastKey = keysHeld();
 
		swiWaitForVBlank();
 
		for(i = 0; i < NUM_STARS; i++)
		{
			EraseStar(&stars[i]);
			MoveStar(&stars[i]);
			DrawStar(&stars[i]);
		}
	}
	return 0;
}

Nothing spectacular as I’m just going through the different tutorials I find on the net. I’m posting these examples for people who are new to the whole DS programming scene and would like to see the examples but with a few extras. I’m still learning how all of it works myself so don’t expect the most well written code!

Jul 17

DS Programming – Messing with basic sound

The second program I made was to test some basic sound functionality. I basically have the DS play a sinewave. The panning can be influenced by moving left or right on the touchscreen, the frequency by moving up or down. Hit the A button to toggle sound on/off. Using the L and R shoulder buttons you can change the dutycycle of the wave.

As always, here’s the code if you would like to give it a try:

#include <stdio.h>
#include <nds.h>
#include <nds/ndstypes.h>
 
int sound = 0;
int sndStatus = 0;
touchPosition TouchStructure;
u16 Pressed;
u16 Held;
u16 Released;
const int FREQ_MUL = 100;
const int VOLUME = 64;
//---------------------------------------------------------------------------------
int main(void) {
//---------------------------------------------------------------------------------
	int xPos = 120;
	int yPos = 40;
	int span = 64;
	int duty = 50;
	consoleDemoInit();
	soundEnable();
	sound = soundPlayPSG(duty,yPos * FREQ_MUL,VOLUME,span);	
	soundPause(sound);
	while(1) 
	{
		swiWaitForVBlank();
 
		scanKeys();
		Pressed = keysDown();
		Held = keysHeld();
		Released = keysUp();
 
		touchRead(&TouchStructure);
		if(TouchStructure.px > 0)
		{
			xPos = TouchStructure.px;
			span = div32(xPos, 2);
			soundSetPan(sound, span);
			iprintf("xPos: %d - pan: %d\n", xPos, span);
		}
		if(TouchStructure.py > 0)
		{
			yPos = TouchStructure.py;
			iprintf("yPos: %d\n", yPos);
		}
 
		if(KEY_A & Pressed && sndStatus == 0)
		{
			soundResume(sound);
			sndStatus = 1;
			iprintf("Sound On\n");
		}
		else if(KEY_A & Pressed && sndStatus == 1)
		{
			soundPause(sound);
			sndStatus = 0;
			iprintf("Sound OFF\n");
		}
 
		soundSetFreq(sound, yPos * FREQ_MUL);
 
		if(KEY_L & Pressed)
			if(duty >= 5)
				duty -= 5;
		if(KEY_R & Pressed)
			if(duty <= 95)
				duty += 5;
 
		soundSetWaveDuty(sound, duty);
 
		if(KEY_B & Pressed)
			iprintf("Freq is %d, pan is %d, duty is%d\n", yPos, span, duty);		
	}
}

Note that for the panning variable I used span, initially I used ‘pan’ but for some reason that didn’t work as pan was always 0 whatever I did with it. I also wasn’t really sure how to have the A button toggle the sound, I didn’t find a function that grabs the status whether the sound is on or off so I added my own variable to keep track of it.

Also notice my FREQ_MUL, as I did with the panning conversion, I added a multiplier since I’m using the y-values of the touching on the screen. Since the screen is only about 200 pixels in height it would be no fun to just have frequencies ranging from 1 to 200 Hz.

It’s really amazing how easy it is to make basic applications that work on the DS thanks to libnds and DevKitPRO. I wonder if it has surpassed the official Nintendo SDK in usefulness after all these years of improving it. It really is a sad thing all of these official SDKs aren’t available for the masses.

The next thing I’m going to give a try is something with graphics. NightFox’s graphics library seems to be quite nice for beginning developers. Thanks to the Grit application converting bitmap files into supported files that the NDS can display is a breeze.