Day 3 - Pad, Stylus, and Text ← Previous

Day 4 - Sprites

Here is the tutorial to learn about sprites.

DS Sprite Specs

Before starting we will see exactly what the DS is capable of ... 128 sprites can shown on each screen or engine, so a total of 256 different sprites on both.

Each sprite can be horizontal or vertical, can be moved across the screen, can be animated (by updating the image used), can become partly transparent, or even become a patchwork!

Also, the sprites can be rotated and zoomed! However, there is a limit to this point ... You cannot set a rotation / zoom setting for each sprite, but there are only 32 different locations to the screen (called rotsets). And then assign each a sprite rotset (or not). Therefore, all the sprites can rotate, zoom, or both, but only in 32 different ways at once. Several sprites can share the same rotset, which is not a problem, and will be rotated and enlarged in the same way. So only 32 sprites can be affine transformed, which shouldn’t bother most people.

DS Screen Sizes

This is a good schema by Bennyboo, which could be useful ... not only for the sprites, but for many other things...

www.palib.info_screens_ds_ds_screen.jpg

As for the sprites, you just consider about the pixel size ... So the max is 256 pixels wide and 192 pixels high. Given that the first pixel is the number 0, not 1, it means 0-255 and 0-191 ... As shown in the diagram.

In addition, the position of a sprite is limited to certain values. X can only be between 0 and 511, and Y between 0 and 255. That means putting a sprite at the position X = 512 is equivalent to putting it in position X = 0! That is what is called sprite wrapping. When it reaches the limit, becomes the other and vice versa.

Color Modes

Now, with regards to the colors ... The Sprites can have 3 different color modes:

16 color palettes, with a total of 16 different screen palettes. This is widely used in GBA, but disappeared in DS. 16 colour sprites can use all 16 palettes, meaning if you decide to, you can choose different colour palettes for sprites, so you create different looking sprites by only using one sprite, just by changing the palette. Each tile of a 16 colour takes up 32 bytes.

256 color palettes (much better, but uses twice as much memory), with a total of 16 types of palettes per screen (the GBA only had 1). This is what we will use mostly... You can only use the same palette.

16-bit sprites, (that is to say, they don’t have a palette). However, these sprites are not used to often, as they use a lot more of video RAM, and takes up far too much space to be used.

In the end, it is best to get sprites that are 256 colors :) You’ll see that even if this seems a bit limited, it will have large enough colors for each sprite.

anandjones

Sprite Sizes

The DS can handle quite a few different sprite sizes, but you have to always remember that only specific sizes are allowed. The only available width and heights are 8, 16, 32, and 64 pixels. And as a few specific sizes aren’t there, here are the sizes you can use (the width is put horizontally, height vertically...) :

8 16 32 64
8 8×8 16×8 32×8
16 8×16 16×16 32×16
32 8×32 16×32 32×32 64×32
64 32×64 64×64

Basically, you can use any sprite size beside 64×8, 64×16, 16×64, or 8×64...

Now, what happens if your sprite isn’t exactly the same size as the authorized sizes ? You’ll get a pretty ugly and unviewable sprite... There is a simple way to go around this, however : using any paint program, just set your sprite size to a DS size in which it fits. For example, if you made a 48×48 pixels sprite, you can fit it in a 64×64 box ! Same thing would apply for like 10×64, but the best size would be 32×64...

Transparent color

Last thing to take into account is that, I can guess, you don’t want the background color of your sprite to be visible... If you have a round sprite, like a frisbee, you wouldn’t want the square border to show. In order for the background to be ‘removed’, or rather set as the transparent color (only 1 transparent color per sprite), you have to choose your transparent color : the best is magenta, as you don’t use it very often (red : 255, green : 0, blue : 255), or some use black, but as I often use black in my sprites, I don’t find that to be such a good choice...

Here are some common colors used for transparency:
**Color name | R,G,B
Magenta | 255,0,255
“Blackread” | 1,0,0
Off Black | 1,1,1
Off white | 254,254,254

Converting with PAGfx

You know what would have been cool ? If sprites could be used as they are, without any modification, directly on the DS... sorry to disappoint you, stop dreaming ! :’( That’s just not how the things are, but you’ll see that converting a sprite to the DS format isn’t THAT complicated...

The tool we are going to use is PAGfx, a converter created by Mollusk and Kleevah, which could hardly be easier to use. There are 2 ways to use it, one for beginners, using the Visual Interface, one for advanced users, with just the PAGfx.ini file. Both ways offer the same possibilities, don’t worry.

PAgfx is now available on Linux! Download latest version hereJosheat 14/2/2007

PAGfx Visual Interface

First things first... Go into your PAlib/Tools directory, and you should see a PAGfx folder. Open it, and inside you should have 4 files.

  • PAGfx.txt is just a quick help and a changelog. You shouldn’t need it really, as I’ll explain everything here...
  • PAGfx.ini is the ini file used for converting sprites, backgrounds, textures... I’ll explain it in the second part
  • PAGfx.exe is the exe file that converts your sprites, based on the ini file... You’ll see that later on too...
  • PAGC Frontend.exe... is the frontend ! Now, that’s the one you’ll want to open right now.

If you get an error message, it’s because you don’t have the .Net framework installed (yeah, sometimes life sucks...). You can just download and install it

Now, when opening the frontend, here’s what you get :

Buttons

I’ll explain the buttons, but it’s pretty self-explanatory...

  • Add Files adds a file to either sprites, backgrounds, or textures, depending on where you are...
  • Remove Files removes the selected files
  • Load INI loads the PAGfx.ini file in the current directory
  • Save INI saves all the sprite/background/texture infos to PAGfx.ini for later conversion with PAGfxConverter.exe
  • Save and Convert just saves to the INI file, and loads PAGfxConverter.exe to convert it immediately, which is what you should use the most...

Next, you see that there are 3 possible tabs : Sprites, Backgrounds, and Textures... nothing too complicated to understand...

Setting the transparency

As I said before, you can choose which color will be the transparent color for you sprite background, and that’s rather important. In PAGC Frontend, you have a Transparent color part, and in it you can choose between 4 colors : black, white, magenta, and green. As said before, I recommend Magenta, but Black is often used.

Adding a Sprite

Ok, to see how it works, we’ll start off by adding a file... just add any image file (make sure it’s in the same directory as the PAGC Frontend!). Let’s see what the options are for the sprites...

  • Filename : the filename ! Duh...
  • ColorMode : This is an important one... you can choose between 16colors mode, 256colors, or 16bit. Basically, what you’ll want the most is 256 color mode.
  • PaletteName : This is the 2nd important one... as seen before, the DS can have 16 palettes per screen for the sprites. By default, the program puts as palette name the sprite name. You can change this to have like sprite0, and for other sprites sprite1, etc... If you want several sprites to have the same palette, just put them the same palette name :-) Can’t make it much easier... The palette’s real name will be PaletteName_Pal, but we’ll see that very soon...
  • Path : just the path... nothing much...

And that’s basically all you need to know ! Oh, and sprites must have width/height multiples of 8, if not it won’t work ;)

Converting

Now, click Save and Convert, it should output some weird info that you might already understand, and if not it’s ok, you don’t care for now, and then end up telling it worked (if your sprites had less than 256 colors... if not, you’re in trouble ! just kidding, if not, use a sprite from the PAlib Sprite examples). If it worked, you should get something like this :

NOTE!!: For some computers, it will not say “Press any key to exit.” This, however, shouldn’t affect the created file(s).

You’ll see that the converter outputed a few files :

  • The SpriteName.c is the file containing all your sprite’s data, and will be used to load the sprite
  • The PaletteName.pal.c contains... the palette ! So you’ll need that too to load your sprite with correct colors...
  • all_gfx.c is a pretty cool file, it actually links to all the sprite, background, and palette files... Why ? because instead of having to add each image.c file to your project, you’ll just have to add this one, and it’ll add everything for you :-)
  • all_gfx.h is pretty much the same as the .c, but contains the sprites’ data names and all, you’ll see that later on...

If you want to, you don’t have to include the all_gfx.c file, just only the header .h. Just have your binary .bin files (outputted in bin folder from PAGfx), and put those in your template data folder. However, at the cost of a longer compile time. But if you want to, what I do to save a lot of time, is just edit my batch .bat file in Notepad, and then just remove the make clean line. Saves a lot of clean time. And then if you want to, just add the line back in. Easy :)

AJ

PAGfx.ini

For those out there who are curious, you can now check the PAGfx.ini that was created by the Frontend... If you just want to use the frontend, don’t bother, and skip this part...

It should be really basic, something like

#TranspColor Magenta
 
#Sprites :
C:\test.png 256colors test
 
#Backgrounds :
 
#Textures :

It’s not hard to understand what all this stands for...

  • #TranspColor Magenta is the transparent color you want to use for sprites and backgrounds. It can be White, Black, Green, or Magenta...
  • #Sprites : will list all the sprites to convert... Each line is like C:\test.png 256colors test
    • C:\test.png is the path towards the image. You can put a relative path, like just test.png if PAGfxConverter.exe is in the same folder...
    • 256colors is the color mode, so 16colors, 256colors, or 16bit, for sprites... (we’ll see backgrounds later on).
    • test is the palette name, I left it by default, so the palette will be test_Pal...

And the rest doesn’t matter much yet, as it’s for backgrounds and textures :-) Simple, isn’t it ? You can convert PAGfx.ini by using PAGfx.exe... It’s the converter called from the frontend

PAlib Sprites

In the first sprite tutorials, we will concentrate on plain, basic sprites, and having them run up and down the screen. Later tutorials will cover sprite rotation/zooms, sprite transparency, and sprite animation. Be patient !

Displaying

The time has finally come to actually use the DS’s hardware ! Yahoo !! Here comes the first sprite displaying tutorial...

The following examples are taken from PAlib v0.72a, because I update the sprite stuff in that version... Please install this version (or above) before continuing, or else you will not have the exact same examples in the PAlibExamples folder...

Open PAlibExamples/Sprites/Basics/CreateSprite, and check out the main.c code in it :

#include <PA9.h>
 
// PAGfxConverter Include
#include "gfx/all_gfx.h"
#include "gfx/all_gfx.c"
 
int main(void){
 
	PA_Init(); //PAlib inits
	PA_InitVBL();
	
	PA_LoadSpritePal(0, // Screen
			0, // Palette number
			(void*)sprite0_Pal);	// Palette name
					
	PA_CreateSprite(0, // Screen
			0, // Sprite number
			(void*)vaisseau_Sprite, // Sprite name
			OBJ_SIZE_32X32, // Sprite size
			1, // 256 color mode
			0, // Sprite palette number
			50, 50); // X and Y position on the screen
 
	while(1) // Infinite loops
	{
                PA_WaitForVBL();    
	}
return 0;
}

As you may see, everything is explained already ! I won’t come back on the stuff explained in Day1 (check out the template decoding part...), assuming you did that already...

// PAGfxConverter Include
#include "gfx/all_gfx.c"
#include "gfx/all_gfx.h"

This part includes all the graphics you have converted and put in source/gfx. Nothing more to say about it (you can check that folder, that’s where the sprite was converted, with its palette and all...)

PA_LoadSpritePal(0, // Screen
		0, // Palette number
		(void*)sprite0_Pal);	// Palette name

This is important : it’s the palette loading ! It loads a palette on the bottom screen (screen 0 is the bottom screen, 1 the top screen...), in the first palette (palette 0), and the palette loaded is sprite0_Pal... Don’t worry about the ‘(void*)’ in front of the palette name, it’s just to indicated that it gives the palette’s location... The top first reason for a sprite not showing is... forgetting to load a palette for it ! and be carefull, load it with the correct number (since there are 16 different palettes for the sprites...)

PA_CreateSprite(0, // Screen
		0, // Sprite number
		(void*)vaisseau_Sprite, // Sprite name
		OBJ_SIZE_32X32, // Sprite size
		1, // 256 color mode
		0, // Sprite palette number
		50, 50); // X and Y position on the screen

Here, everything is explained again, this example really is easy ! :-P

  • Screen 0 for bottom screen, again... If you load on the top screen, the sprite will not show. Why ? because the palette was loaded for the bottom screen !
  • Sprite number comes next... As said before, the DS can have up to 128 sprites on each screen, numbered from 0 to 127 (included). For each sprite you create, you therefore have to choose a sprite number, which will be used later on to modify that sprite (update it, move it around, etc...). Furthermore, the sprite number determines which sprites are in front of which (sprite 0 is in front of all sprites, 1 in front of all besides 0, etc...) The lower the number, the higher the priority it has.
  • The obj size is composed of 2 variables which aren’t actually easy to use, so the macros OBJ_SIZE are there to help... If you are unsure what sizes are available, check the beginning of this tutorial...
  • The color mode comes next. It can be only 2 things : 0 for 16 colors, 1 for 256 colors. You’ll almost always use 256 colors... (The code to display a 16 color sprite is not the same as the 256 color one.)
  • Then comes the palette number. Here, it’s 0... If you put the wrong number, you will either get a sprite with odd colors (if it uses another palette), or no sprite (no palette loaded...). So be carefull when using that one.
  • Then comes the X and Y coordinates of the sprite, in pixels, nothing much. This coordinate does not use the sprite’s center, but the upper left corner.

And that’s basically all there is to understand ! Once you got that, just double-click the build.bat, and open the rom in the emulator or on DS... As the infinite loop (while(1)...) does not contain any code (other than the VBL), your sprite will just sit there and not do anything.

Before going on to the next part, try modifying this example to have it display the sprite on the top screen, then on both screens...

For a 16color sprite, you have to use the following:

The palette:

	PA_LoadSprite16cPal(0, // Screen
			    0, // Palette number
			   (void*)sprite0_Pal);	// Palette name

And for creating the sprite:

	PA_CreateSprite(0, // Screen
					0, // Sprite number
					(void*)vaisseau_Sprite, // Sprite name
					OBJ_SIZE_32X32, // Sprite size
					0, // 16 color mode
					0, // Sprite palette number
					50, 50); // X and Y position on the screen

Moving

Here comes what could be the most important aspect of sprites... moving them around ! We’ll see 2 different methods, the first using the stylus, and the second being the universal method you’ll use all the time...

PA_MoveSprite

Ok, first, open the MoveSprites example in PAExamples/Sprites. We won’t paste all the code this time, as tons of it are similar to what you’ve already seen...

for (i = 0; i < 16; i++) PA_CreateSprite(0, i,(void*)vaisseau_Sprite, OBJ_SIZE_32X32,1, 0, i << 4, i << 3);
// This loads sprites a bit everywhere

Ok, now, why is there a for before the the CreateSprite function ? Simply because we would like to create 16 sprites (just for testing), similar... As you can see, the sprite number is... i ! That means that we will create sprites with numbers 0 to 15. Last, you see that the x and y positions are (i«4) and (i«3). I won’t explain that too much just yet, but it means i*16 and i*8. There’ll be a tutorial later on, in the math part, concerning these stuff... Why i*16 ? Because the first sprite (i = 0) will be at 0, the second at 16, etc... So we’ll have sprites all over the screen! Cool!

Next comes, in the main loop, the important code you want to memorise :

while(1)
{
	
	// Use the MoveSprite function on all sprites...
	for (i = 0; i < 16; i++) PA_MoveSprite(i);
	// The MoveSprite function checks if you are touching a sprite, and moves it around if*    
        // *you are... Pretty nice if you have multiple sprites around
 
	PA_WaitForVBL();
}

This is a basic main loop, with just a single function in it : PA_MoveSprite... now, this function couldn’t get any easier; This function checks if the given sprite number, on the bottom screen, is a sprite which is touched by the stylus... If that’s the case, it’ll bind with it, and they become linked. As long as you keep the stylus held, the sprite will move to its position :-) So why is the for loop here again ? Because we want to test sprites 0 to 15! Yup, that simple...

You can now compile the code and test it on your DS, because it crashes Dualis r12 (not r11)... You’ll see this simple code can give a pretty good result, and wasn’t much effort !

PA_SetSpriteXY

Here comes the mighty most important function concerning sprites !! The SetSprite function !! It’s use is pretty simple and basic :

PA_SetSpriteXY(screen, sprite, x, y);

Could it be easier to understand ? You just have to give it which sprite to move (screen (0 for bottom, 1 for top screen) and sprite number), and then the new position (x and y), and the sprite is assigned this new position. There is only one thing you really need to know : the position corresponds to the sprite’s upper left corner, not the center. So if your sprite is like 32×32, you’ll want to put as coordinates x-16 and y-16 to have it centered on point x, y.

On another note, you can also use PA_SetSpriteX and PA_SetSpriteY if you do not want to set the x and y positions at the same time, it goes faster...

X and Y limits

There are 2 more functions available : PA_GetSpriteX(screen, sprite) and PA_GetSpriteY(screen, sprite) which return the sprite’s coordinates in PAlib’s sprite system... The first thing you’ll notice is that these values will always range between 0 and 511 for X, 0 and 255 for Y, because these are hardware limits... Considering this, if you use values above or below these, the sprite will wrap around... A sprite at position x = 512 will be given the position x = 0, and will therefor be on the screen ! If the value of X is set to a negative number, the sprite wraps the other way. That is, a sprite of width 10 with an X value of -10 will be invisible as its placed ot position (512-10= 502) which is off screen. Same thing applies for Y, but with more limited values...

Keys

Now that you know how to use this function, the first thing we’ll see is how to move a sprite with the DS’s cross ! Now, let’s check the code in PAExamples/Sprites/MoveSpritewithKeys...

I will only paste the important lines :

s32 x = 0;    s32 y = 0; // sprite position... 
 

These are 2 variables (we could have used structures now that you know how to use them :-P) that will store the sprite’s position...

x += Pad.Held.Right - Pad.Held.Left;
y += Pad.Held.Down - Pad.Held.Up;

What’s this ??? I know that it’s not really what comes to mind concerning sprite movement. The easiest way to do it would have been like

if (Pad.Held.Right) x = x + 1;

(to move the sprite 1 pixel...)

Here, what happens if you press Right ? Pad.Held.Right gets value 1, and Pad.Held.Left gets value 0. So

x += Pad.Held.Right - Pad.Held.Left;   -> x += 1 - 0;

So it moves the X position by 1 pixel, exactly like the if stuff did, except that this works for Left (-1 pixels) and does not use the if, which is slow !

Same thing for up and down...

Now, we’re getting to the last part of the code :

PA_SetSpriteXY(0, // screen
		0, // sprite
		x, // x position
		y); // y...

Pretty easy, already explained... Now that X and Y positions have been updated according to the Pad, we move the sprite to that position !

You are now free to compile this code and test (works even in dualis...)

For those who want to go a little further, how can you change the movement’s speed ? Say you want to move by 2 pixels instead of 1...

...

..

.

Time up ! The correct answer is :

x += (Pad.Held.Right - Pad.Held.Left) * speed;
y += (Pad.Held.Down - Pad.Held.Up) * speed;

(speed can be any value, such as 2 for 2 pixels...) If you don’t believe me, just test it, replace speed by the number of pixels to move per frame and you’ll see the sprite moving around faster :-P

Stylus

To check this one out, just open the MoveSpriteWithStylus example, and you’ll see it’s even easier to understand than the previous one ! Basically, there’s just 1 important line of code (the rest is sprite loading and displaying the stylus’s position on the screen...)

PA_SetSpriteXY(0,0,Stylus.X,Stylus.Y);

It puts sprite 0, (on the bottom screen) to the stylus’s X and Y coordinates... nothing more to say about it I guess :-)

With all this, you have enough knowledge to begin programming on your own. If you just started DS dev, I would recommend moving on to the next tutorial (backgrounds) before finishing this one... Or you could just read the rotation part and then move on. If you’re an experienced programmer, you’re welcome to continue on to the end of this tutorial...

Moving without wrapping

With a little help from Mollusk, I’ve finally devised a way to move a sprite without wrapping it. This is useful especially in large maps where you have for example character sprites or something that has to scroll out of view and return when you come visit it again. There are several much more complex ways to do this, but you’re probably going to use something like this as a beginner. Also note than in the Platform Game 5, the coins are deleted when they exit the screen and then created again once you stumble upon them. This is useful if you have lots of spare processing time (meaning you don’t have too much going on) and not enough memory to store every sprite. But sometimes, you have lots of memory, so the sprites can just stay there. You want this, for example, in shared multiplayer because one player can be within range to see the sprite, and another player could be too far away from it. This just keeps the sprite there for everyone to see. Keeping the sprite there also makes sense if you don’t have a lot of sprites, and you could easily spare those few kilobytes of VRAM. Lets take a look at a classic no wrapping function:

void SetSpriteNoWrapX(u8 screen, u8 sprite,,u8 size, s16 x) {
    if ((x < -(size-1)) || (x >= 256)) {
        PA_SetSpriteX(screen, sprite, 256); //If the sprite is out of bounds, move it offscreen
    }
    else {
        PA_SetSpriteX(screen, sprite, x); // Otherwise, move it to the desired position
    }
}

If you’re as smart as me (lol), you’d care to point out that if you’re scrolling around, the sprite can’t have a position in terms of screen pixels, because otherwise it would just stay there and not move. Therefore, you must have your sprite positioned on a scroll grid. Scroll grids, as I call them, automatically change the positions of objects as you scroll around. Although the scroll grid also uses pixel-based values, it is much more flexible as it can scroll the object in question off the screen. More about scroll grids in Day 6.

-Phaezon (martin.hanzel@live.ca)

Stylus Touch

PAlib offers a function or 2 to know if a given sprite is touched by the stylus. This can be used to activate some functions in a game or application. Note, however, that doing such a function yourself would be faster, as it would be perfectly adapted to your game, whereas PAlib’s function must be general and work for everyone...

The example in PAlib is SpriteTouched.

u8 i = 0; 
for (i = 0; i < 8; i++) PA_CreateSprite(0, i,(void*)mollusk_Sprite, OBJ_SIZE_32X32,1, 0, i << 5, i << 4);
 
PA_OutputSimpleText(1, 0, 10, "Please touch a sprite");
 
while(1)
{	
	// Now we'll test every sprite to see if we touch it...
	for (i = 0; i < 8; i++) {
		if (PA_SpriteTouched(i)) PA_OutputText(1, 0, 15, "Sprite %d  ", i);
		// If we touch the sprite, returns 1...
	}
 
	PA_WaitForVBL();
}

The first 2 lines just create sprites at different positions... That’s the same as the Sprite Moving example, nothing much to say about it...

But in the infinite loop comes the interesting code : PA_SpriteTouched(sprite). This simple function returns 0 if the sprite isn’t touched by the stylus, and 1 if it’s touched... This is enough to test one sprite at a time, but here we had 8 different sprites. That’s why I added the for loop, which will here test for sprites from 0 to 7, and display on the screen which sprite was touched. You could replace the text output by something like spritetouched = i;, which would store the touched sprite’s number in the spritetouched variable...

Nothing more to say about this example, it’s just pretty simple and basic :-) Hope you understood everything !

Rotation and Zoom

Here comes another important tutorial, this time concerning rotations and zooms ! As I may have said before, sprites can be rotated and/or zoomed at will, but there is a small limitation : even though all the sprites be rotated, a rotated sprite needs to have a rotset attributed to him, and only 32 rotsets are available per screen or only 32 affine sprites... Then how can all the sprites be rotated, if only 32 rotsets are available ????? Well, you can have several sprites on the same rotset, and, in that case, all these sprites will be rotated/scaled with in the exact same way ! Imagine you have a space ship, which can turn around, and is made up of several parts (let’s say 2 sprites for the wings, 1 for the body, 1 for the cockpit, and 1 for the guns...). They all need to be rotated and scaled in the exact same way, so instead of having each a different rotset, it’s just easier to give them all the same one :-)

There are 3 examples for rotations and zooms in the PAlib Sprite Examples : Rotation, Zoom, and RotZoom, which use 3 different functions, one for only rotating, one for only zooming, and one which does both at the same time...

Rotation

We’ll start with Sprite Rotations... The example is Sprite_Rotation, and I’ll paste here the interesting part of the code :

// Activate rotations for that sprite
PA_SetSpriteRotEnable(0,// screen
		      0,// sprite number
		      0);// rotset number. You have 32 rotsets (0-31) per screen. 2 sprites with*
                         // *the same rotset will be zoomed/rotated the same way...
 
u16 angle = 0; // Rotation angle...
 
while(1)
{
	++angle; // change the angle
	angle &= 511; // limit the range to 0-511. works only with 1, 3, 7, 15, 31, etc... (2^n  - 1)
	
	// Fast function for rotations without zoom...
	PA_SetRotsetNoZoom(0, //screen
			   0, // rotset
			   angle); // angle, from 0 to 511
	
	PA_WaitForVBL(); // Synch
}

First off, you create a sprite, just as usual, with its palette and all... Then, you need to activate the sprite in rotation/zoom mode and give him a rotset (from 0 to 31, remember ?), which is done with PA_SetSpriteRotEnable(screen, sprite, rotset). Once this is done, the sprite is ready to rotate ! When you want to stop rotating it, you can use PA_SetSpriteRotDisable(screen, sprite).

The few lines of code directly following aren’t too important, it’s just declaring a variable for the angle, and adding 1 ‘degree’ to it each turn... There is, though, a major thing to know about the angle. It’s not a 360° angle, but ranges from 0 to 511, and it’s counterclockwise... Why ? because that’s what is best for the DS, and it’s much faster that way than with normal angles... You’ll get used to it :-) So what’s the angle &= 511; all about ? You’ll have a more complete tutorial on the & operation in the math tutorial coming up. All you need to know is that it limits the variables range to that value, but only takes 2^n - 1 numbers : 1, 3? 7, 15, 31, 63, 127, 255, 511, etc... So it limits the variable’s range to 0-511, which is exactly our angle’s limits ! Hehehe... What happens if the value goes over 511 ? It goes back to 0, and so on. So 512 will give 0, 513 will give 1, etc... And that’s true for negative values, -1 giving... 511 ! This is cool (and one of the reasons we don’t use 360° angles).

Next comes the important function, PA_SetRotsetNoZoom(screen, rotset, angle); . Never forget that here, you are manipulating the rotset (0-31) and not the sprite, that’s why you don’t use the sprite number anymore... You just give the screen, the rotset, and the angle (0-511) as arguments, and PA_SetRotsetNoZoom will rotate the sprite just like you want !

As this example has an angle to which 1 is added every frame, you can compile the example and see on hardware how it turns... It can work on DualiS... but doesn’t always, sorry.

Some helpful functions: By Prob_Caboose

float NDSRotToAng(float input){
	return ((input*100)/142);}
	
float AngToNDSRot(float input){
	return ((input*142)/100);}

Last thing : the sprite is rotated from it’s central point :-)

Zoom

Now that we have seen how to rotate a sprite, we’ll see how to zoom it. It’s fairly easy, once again, and the example, Sprite_Zoom, is really similar to the one we’ve just seen... I won’t paste the Rotset Enabling part of the code this time, you already saw it :-P, right ?

u16 zoom = 256; // Zoom. 256 means no zoom, 512 is twice as small, 128 is twice as big....
	
while(1)
{
	zoom -= Pad.Held.Up - Pad.Held.Down; // Change the zoom according to the keys...
 
	// Fast function for zoom without rotations...
	PA_SetRotsetNoAngle(0, //screen
			    0, // rotset
			    zoom, zoom); // Horizontal and vertical zoom. You can have a sprite*
                            // *streched out if you want, with the zoom only for x or y axis....

First off, you have the zoom variable... Why is it set to 256 ? Because... 256 means NO zoom. The DS zoom works the following way :

  • 256 is no zoom, or rather 100% size...
  • 512 is half zoom, 50% size...
  • 128 is double zoom, or 200% size...

I know this is a bit painful at first, but you’ll get use to it. To make it simple, the base zoom is 256, smaller values give a bigger sprite, and bigger values give a smaller sprite. Here, the zoom is modified by the Pad Up and Down keys, so you can check for yourself the result...

Now, let’s check the important zoom function : PA_SetRotsetNoAngle(screen, rotset, zoomx, zoomy);. Why are there 2 zoom values ??? Because you can zoom your sprite independently on the X and Y axis if you want ! Even though most of the time you’ll want to zoom it the same way on both axis, it can be good to have different zooms... I used it in a space ship demo, to stretch the ships vertically and flatten them horizontally, which made a nice squeeze effect during hyperspace.

Just compile and test ! You’ll see how this looks like :-) The sprite is zoomed from the central point, just like for the rotations...

If you’re smart (or if you were careful when reading this tutorial and the Zoom example in PAlib), you should have 2 questions, which are in fact pretty much the same :

  1. What happens if a sprite is zoomed too big ? After all, when creating the sprite, you set its size, so having a too big sprite should go beyond this size, and... not work ??
  2. Why didn’t I talk about PA_SetSpriteDblsize(0, 0, 1);, which is in the PAlib example ??

Well, first : if your sprite is too big, everything beyond the sprite frame (like 32×32, for example), will just be cut off ! Ok, this is very limiting, but hey, Nintendo thought of everything ! And that’s where question 2, PA_SetSpriteDblsize(screen, sprite, enable/disable) comes in ! If you double-size a sprite, it won’t actually zoom it to 200%, but double’s it’s frame size ! So a 32×32 sprite becomes a 64×64 (with the 32×32 image in its center), and a 64×64 becomes... 128×128 ! Wow, that’s a big sprite :-P

Now that you know this, you see how to go beyond the sprite size limit if you want to zoom your sprite a little more... You have to consider one last thing, though : if you double the canva size, and since the sprite position is the top left corner... it changes the sprite’s center on the screen... So if you place 2 sprites at the exact same coordinates, but one double-sized and one normal, you’ll see they’re not placed the same way... Never forget that !

Both Rotating and Zooming

These 2 examples just taught you how to rotate or zoom, but in fact you can do both at the same time if you need to... The PAlib example is the Sprite_RotZoom one, and it’s very much like the other ones, once again, but contains both the angle and zoom variables... I will not post any code this time, as it’s useless, just look at the example :-)

The only function, this time around, is PA_SetRotset(screen, rotset, angle, zoomx, zoomy);. It’s just like a combination of the 2 preceding functions, nothing terrible !

I guess you are now set and know all you need to know about sprite rotations and zooms ! Off to another lesson...

Sprite Flipping

Sprite flipping is something really simple on DS, so even though it could seem like a minor subject, I’ll show how easy and important it can be... I’ll add an example in the next release (Sprite/Flips).

There are just 2 functions to flip a sprite :

  • PA_SetSpriteHflip(screen, sprite, 1/0 for yes/no), for horizontal flips (left/right)
  • PA_SetSpriteVflip(screen, sprite, 1/0 for yes/no), for verticalflips (up/down)

This is really too easy for you ! 1 sets the flip on, 0 removes the flip, that’s it. You can flip horizontally, vertically, or both at the same time.

Now, where/when can this be used ? Imagine you’re doing a fighting game... Like Street Fighter 24532 AlphaBetaGamme XX. You have one player looking towards the right, and one towards the left. There would be 2 ways of acheiving this :

  • Having twice as many graphics, one for left and one for right... This is a lot of unnecessary work and takes up tons of space. It is also slow to update...
  • Using Horizontal Flip to invert left/right ! Fast and Simple...

What seems like the best solution ? :-P

Mosaic Effect

The Mosaic is an easy effect to use, but I don’t like it that much :-P It was used in Super Mario Bros, for example, and changes a bit the way the sprite looks by replacing single pixels by squares of a given size (from 1×1 blocks to 15×15 pixel blocks). What’s so good about that ? It can be used for transition effect on sprites, a sort of blurring effect, etc... But as I said, I don’t like it that much and never use.

Check the Mosaic sprite example in the next version of PAlib coming out...

Anyways, the DS and PAlib offer you a very simple control over the Mosaic effect. Once you have created a sprite, you can activate the effect for a given sprite using PA_SetSpriteMosaic(Screen, Sprite, Mosaic on/off (1/0));.

Once this is done, you can easily change the mosaic values, horizontally and vertically (so you could have like 1×8 pixel blocs if you want !) using the following function : PA_SetSpriteMosaicXY(screen, horizontal block size, vertical size...);

Have you seen the BIG limitation of this ? Well... You can control each sprite to activate or deactivate the mosaic effect, but the mosaic block level is the same for all activated sprites on a given screen ! So you can’t have one sprite with a certain mosaic, and another with a different one... that’s the second reason why I don’t like this effect that much...

Just compile the example and test on DS, should work (though it doesn’t on DualiS :-().

Transparency

Transparency, or Alpha-Blending, can be nice in a game or application. It looks great and professional, and in fact isn’t hard to do !

PAlib uses the DS’s hardware to achieve it, but this has 1 important limitation : even though you can set any sprite you like to be alpha-blended, ALL the alpha-blended sprites will have the same transparency level... So you can’t have like one sprite half-transparent and another almost completely... They’ll all be the same...

The code is fairly easy to understand, and you have an AlphaBlending example in the sprite examples...

PA_SetSpriteMode(0, // Screen
 		 0, // Sprite
		 1); // Alphablending
	
 
s16 alpha = 7; // Transparency level
 
// Enable the alpha-blending
PA_EnableSpecialFx(0, // Screen
		   SFX_ALPHA, // Alpha blending mode
		   0, // Nothing
		   SFX_BG0 | SFX_BG1 | SFX_BG2 | SFX_BG3 | SFX_BD); // Everything normal
 
while(1) // Infinite loops
{
	alpha += Pad.Newpress.Up - Pad.Newpress.Down;
	PA_SetSFXAlpha(0, // Screen
			alpha, // Alpha level, 0-15
			15); // Leave this to 15
 
	PA_WaitForVBL();
 
}

The sprite is created like any normal sprite, but has it’s alphablending option activated with PA_SetSpriteMode(screen, sprite, mode); There are different modes, you can check the documentation, but for now just remember that 0 is nothing special, and 1 is alphablending...

Once activated, the sprite isn’t alphablended just yet. You first need to activate the DS special effect system, setting it to alphablending mode too... PA_EnableSpecialFx(Screen, SFX_ALPHA (Alpha blending mode), 0 (leave to 0 for now), SFX_BG0 | SFX_BG1 | SFX_BG2 | SFX_BG3 | SFX_BD); The big list is a list of everything besides the sprites, don’t worry, you’ll understand that some other day...

PA_SetSFXAlpha(screen, alpha level (0-15), normal level (leave to 15)); is the last important function (the alpha variable I put is modified by the Up and Down keys...), and is used to set the transparency level... Just compile and test different values by pressing Up and Down to see what the sprite looks like...

Frames

What are frames for ? They can be used to animate a sprite in complex ways, to bring it to life ! I’ll talk about sprite animations really soon, because frames are in some way easier to use, but also more flexible... Mastering them fully, however, can take some time.

First thing : if you want to use frames, you must put all the sprite frames in a single image when converting, and these frames must be one on top of the other, like this :

That’s the secret to nice animations... This example will not show any animation, but the most common and usefull use of frames : having the image change depending on the key presses... In this example, the sprite will look in the direction pressed, which is what you need to have it come to life !

PA_CreateSprite(0, 0,(void*)frames_Sprite, OBJ_SIZE_16X32,1, 0, 128-16, 64);
 
while(1)
{
	if (Pad.Held.Up) PA_SetSpriteAnim(0, 0, 0); // screen, sprite, frame
	if (Pad.Held.Down) PA_SetSpriteAnim(0, 0, 2); // screen, sprite, frame
	if (Pad.Held.Left) PA_SetSpriteAnim(0, 0, 3); // screen, sprite, frame
	if (Pad.Held.Right) PA_SetSpriteAnim(0, 0, 1); // screen, sprite, frame		
 
	PA_WaitForVBL();
}

As you can see, the sprite is created like any normal sprite ! That’s what’s cool... Then, you see the code to change the frame, PA_SetSpriteAnim(screen, sprite, frame number);. And that’s it ! In this code, that frame is changed depending on the keypress, in order to have the sprite look at the right direction...

Nothing more to say, so we’ll move on to sprite animations !

Oh, one thing you need to know before continuing... Changing the sprite’s frame means copying the new image over the old one. Which means that it takes time to update. So if you have too many frames updated each turn, the game will slow down...

Animations

Simple Animations

Sprite animation has just become really easy... Now, you can just load a sprite, and tell PAlib to animate it, from one frame to another, and it will automatically loop. Just like the frame choice, you must always put all the images in the same sprite image file, one on top of the other. Here’s the image used in this example :

I’ll just post the interesting part of the code :

// Load the sprite palette, 
PA_LoadSpritePal(0, // Screen
		0, // Palette number
		(void*)explosion_Pal);	// Palette name
	
// Here, we'll load a few similar sprites to animate... at different speed
PA_CreateSprite(0, 0,(void*)explosion_Sprite, OBJ_SIZE_64X64,1, 0, 0, 64);
PA_CreateSprite(0, 1,(void*)explosion_Sprite, OBJ_SIZE_64X64,1, 0, 64, 64);	
PA_CreateSprite(0, 2,(void*)explosion_Sprite, OBJ_SIZE_64X64,1, 0, 128, 64);	
PA_CreateSprite(0, 3,(void*)explosion_Sprite, OBJ_SIZE_64X64,1, 0, 196, 64);		
	
// Start the animation. Once started, it works on its own !
PA_StartSpriteAnim(0, // screen
		0, // sprite number
		0, // first frame is 0
		6, // last frame is 6, since we have 7 frames...
		5); // Speed, set to 5 frames per second						
PA_StartSpriteAnim(0, 1, 0, 6, 15); // for the second one, speed of 15 fps...	
PA_StartSpriteAnim(0, 2, 0, 6, 30); // for the third one, speed of 30 fps...	
PA_StartSpriteAnim(0, 3, 0, 6, 60); // for the last one, speed of 60 fps...

Why does it load 4 sprites ? Just because I wanted to show how it does with 4 different animation speeds, that it... As you can see, the sprite loading is just a plain usual one, nothing special about it, as always... Next, the PA_StartSpriteAnim(screen, sprite, first frame, last frame (included !), speed (in fps)) function. Once loaded, it animates the sprite just like you asked, at the given speed, and indefinitaly. You can use PA_StopSpriteAnim(screen, sprite) to stop it, or PA_PauseSpriteAnim(screen, sprite, pause (1 to pause, 0 to unpause)) to pause/unpause the animation...

You can compile this example and test on DS !

Complex Animations

This second animation example and tutorial doesn’t use more functions than the first one, but rather shows you how to use them better... It’s the SpriteAnim2 example...

The image is similar to the frame tutorial one, but with more frames, to have the movements ! You can check it out in the source/gfx folder of the example. You’ll notice that the sprite has some animations to move up, down, and right... but not left ! Why ? Because the left animation is a copy, flipped, of the right animation... So adding it to the image would take up more space for nothing, it’s just easier to flip the sprite using the DS hardware... Saves up 25%, which is actually alot, considering that the graphics take up the major part of a DS rom...

On to the code we go !

while(1)
{
	// Animation code...
	if(Pad.Newpress.Up) PA_StartSpriteAnim(0, 0, 0, 3, 6);
	if(Pad.Newpress.Down) PA_StartSpriteAnim(0, 0, 8, 11, 6);		
	
	if(Pad.Newpress.Right) {
		PA_StartSpriteAnim(0, 0, 4, 7, 6);	
		PA_SetSpriteHflip(0, 0, 0);
	}
	if(Pad.Newpress.Left) {
		PA_StartSpriteAnim(0, 0, 4, 7, 6);	
		PA_SetSpriteHflip(0, 0, 1);
	}
	
	if(!((Pad.Held.Left)||(Pad.Held.Up)||(Pad.Held.Down)||(Pad.Held.Right))) PA_SpriteAnimPause(0, 0, 1);
 
 
	// Moving Code
	y += Pad.Held.Down - Pad.Held.Up;
	x += Pad.Held.Right - Pad.Held.Left;		
	PA_SetSpriteXY(0, 0, x, y);
	
	PA_WaitForVBL();
}

The sprite is loaded like any sprite, so I didn’t show it here...

if(Pad.Newpress.Up) PA_StartSpriteAnim(0, 0, 0, 3, 6); This means to start playing the animation from frame 0 to 3 when the key up is pressed... The fps is really slow : 6 frames per second ! Why ? Because the animation has very few frames, so it would be way too fast with a higher frame rate. This code is absolutely not perfect, as it doesn’t give good results if you hold different directions at the same time, but it’s just the simplest code I could do... if you need something better, you should now be able to do it by yourself :-P

if(Pad.Newpress.Right) {
	PA_StartSpriteAnim(0, 0, 4, 7, 6);	
	PA_SetSpriteHflip(0, 0, 0);
}

This part is different from the Pad Up/Down, because it checks if left or right to Flip the sprite horizontaly or not (remember, to save space in the rom...). That’s why it has the sprite animation and the Hflip function in it...

I won’t detail the moving code, it’s the one we used before in the movement tutorial...

Compile and test !

Extended Animations

Latest addition to PAlib’s animation system : extended animations ! What is that ??? It allows you to controle easily how the animations are played. When loading them with the AnimEx function instead of the plain Anim one, you have 2 extra options :

  • Animation Type, ANIM_LOOP (0) being normal, and ANIM_UPDOWN (1) being a back and forth animation
  • Cycle Number, being how many times you want it to be played. Setting it to -1 will make it play indefinitly... Concerning the ANIM_UPDOWN, a complete back and forth animation counts as 2 cycles...

As you can guess, the normal animation is just like the extended one, but set to a default value of infinite looping...

The code is a copy/paste of the explosion code, only the starting function changed (and not for all, lol).

// First animation will be normal
PA_StartSpriteAnim(0, // screen
		0, // sprite number
		0, // first frame is 0
		3, // last frame is 3, since we have 4 frames...
		5); // Speed, set to 5 frames per second		
						
// Extended animations for the rest									
PA_StartSpriteAnimEx(0, 1, 0, 3, 5, ANIM_ONESHOT); // just play it once...
PA_StartSpriteAnimEx(0, 2, 0, 3, 5, ANIM_UPDOWN, -1); // back and forth, infinite number of times	
PA_StartSpriteAnimEx(0, 3, 0, 3, 5, ANIM_LOOP, 5); // Play it 5 times

The ANIM_ONESHOT macro is there to say you want to play the animation just once. When it ends, it returns to the first frame... If you would like it to stay at the last frame, use the ANIM_UPDOWN type and 1 cycle...

Compile and enjoy !

Depth

Often, in 3D like games, you need sprite overlapping. People need to go in front other people, etcetera. In order to do this, you have to set priorities. There are 2 kinds of priorities on the DS :

  • Sprite Priority : a sprite with a lower sprite number will be in front of a sprite with a higher sprite number
  • Background Priority : To override the sprite priority, you can change the background priority. By default, all sprites are in front of background 0. You can set a sprite to be in front of a different background, 1, 2, or 3. A sprite on background 2 will be behind all sprites with a background priority of 0 or 1, and in front of all sprites with a priority of 3, all this regardless of their sprite numbers...

I won’t post any code regarding this, just the function : PA_SetSpritePrio(screen, sprite, priority); That’s pretty much all there is to know :-)

Mollusk 29/11/2005 17:42

Dual Sprites

One of the latest additions to PAlib has been the creation of the Dual Sprites functions. I must admit I was a bit against it, but it was so requested I ended up doing it anyways... It took me quite a long and boring time, because it was mainly just copy/paste and minor modifications to all the sprite functions.

Enough talk about me... What are these... Dual Sprites... ??? Well, they’re a modified version of the sprites, which display can be displayed on both screens as though it was just one ! This doesn’t mean a sprite will be displayed on the top and bottom screens at the same time, but that instead of having a vertical range of 0-191, it becomes 0-383, by using both screens as a single ! So you can have your sprites moving back and forth from one screen to another very easily. This is nice for games like pong, shoot’em up, etc...

The y = 0 point is the top-left point on the top screen. Pretty much all the functions we have already seen are available using PAlib’s Dual Sprite system, just by adding the prefix Dual : PA_DualCreateSprite(...), PA_DualSetSpriteXY(sprite, x, y), etc... The only difference, besides the Dual prefix, is that these functions do NOT require you to set a screen number anymore... seems logical, since you consider them like just a single screen...

An example exists now. See %PALIBEXAMPLES%\Sprites\Basics\DualSprite\

Last thing, but it’s important : you probably noticed that the DS’s screens don’t touch... they have a space in between them, and so I wondered how to manage that space in the Dual functions... My final descision was to go for a more flexible system, so I added a last function : PA_SetScreenSpace(space in pixels). By default, this value is 48 pixels, it’s the value I felt the most comfortable with... This means that when moving a sprite from one screen to another, it moves as though there were 48 pixels in between, during which you DO NOT see the sprite if it’s too small... Of course, a 64×64 sprite will always show, as it’s bigger than the 48 pixel space... If you want to do a pong-like game, you can leave that value to 48, or even put 64 to have a little more space. But if you are doing a shoot’em up, you might like to not have any space between the screens, so that NO objects can be hidden from the player. In that case, go for a 0, or 16, pixel space ; 0 will act as though the screens touched themselves...

DS Sprites Explained

I guess there’s a small part that was missing here : how the sprite system works... Knowing this will help you decide how to organize your graphics and optimize your code...

What is a sprite ? It’s not just a single entity, but a mixture of several things :

In 2D programming, it is usually referred to an 2D image or animation. The Nintendo DS has dedicated 2D hardware which makes it very easy to use these.

1. The image, in ‘raw format’, which is your Name_Sprite array, converted using PAGfx or gfx2gba, grit or anything else... That’s basically the graphics, which will be copied in the VRAM (Video Ram), in a specific part reserved for sprites (1 part for top screen, 1 part for bottom screen). Several sprites can share the same graphic, but as it has only 1 copy in that case, animating 1 sprite will change the graphics in the VRAM and thus animate all sprites the same way...

2. The sprite palette, which is shared for all the sprites. PAlib, by default, has 16 palettes for the sprite, so you could have 16 sprites with different palettes, or 10 with the first and other with the 15 others, etc... But in lots of cases (especially when you start out), you just use a single palette for all the sprites...

3. The DS knows all about the sprites thanks to the OAM (Object Attribute Memory), which is a place in memory to handle all the sprites. This is a huge array containing the basic info for all sprites, one by one... (numbered 0 to 127, 2 copies existing, one for each screen...). The infos are like : X and Y position, shape/size (square/rectangle, 8 pixels wide or more...), horizontal or vertical flips, etc... One important info : rotation/zoom on/off, and Rotset number (0 to 31).

4. The rotset contains how the sprite using that rotset will be zoomed/rotated. As there are only 32, you cannot have 128 sprites rotated 128 different ways :/ So only 32 sprites are available to be affine transformed. Seems like a limit, but in fact it doesn’t matter that much, you can most of the time find a decent solution : several sprites can share the same rotset if needed to be rotated/zoomed identically... And you won’t find that you would have more than 32 sprites anyway for most projects.

AJ

FAQ - Sprites

Q1. My sprite doesn’t show up !!

  • Ok, a few things to check...
    • Have you loaded a palette to go with it ?
    • Is this palette on the same screen as your sprite ?
    • Do the palette and the sprite have the same palette number ?

Q2. My sprite has a background behind it!

  • Check these couple of things
    • Look at question 1’s answers
    • Make sure that the transparency colors are correct. If you’re using Adobe Photoshop or any other ‘professional’ photo editing tool, the background color in some places can be off just by a minuscule amount. This is because these tools blend color in some places to reduce jagged edges, so you might get a tiny shade of the background color where you don’t want it. The image converter is very picky about colors, so even if a pixel is just a tiny bit off, it’ll judge is as a non-background color. This color difference might be totally unnoticeable to human eyes, so I prefer to go over the edges in a program like Paint (lol) which doesn’t have anti-alias (edge blending). However, if you still feel like using a professional tool, you can turn off this feature. On Paint.NET, turn off anti-alias for the tools. On GIMP, use the pencil tool instead of the brush tool.

Next → Day 5 - Backgrounds

Mollusk 28/11/2005 00:35

 
day4.txt · Last modified: 19/08/2009 22:39 by 84.28.158.92
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki