Day 3 - Pad, Stylus, and Text ← Previous
Here is the tutorial to learn about sprites.
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.
This is a good schema by Bennyboo, which could be useful ... not only for the sprites, but for many other things...
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.
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
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...
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
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 here — Josheat 14/2/2007
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.
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 :
I’ll explain the buttons, but it’s pretty self-explanatory...
Next, you see that there are 3 possible tabs : Sprites, Backgrounds, and Textures... nothing too complicated to understand...
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.
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...
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 ;)
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 :
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
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...
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
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 !
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 !
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
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...
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 !
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...
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...
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
) 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
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...
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)
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 !
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...
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
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
, 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 :
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 :
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
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 !
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 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 :
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 :
What seems like the best solution ?
The Mosaic is an easy effect to use, but I don’t like it that much
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, 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...
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...
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 !
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
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 !
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 :
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 !
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 :
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
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...
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
Q1. My sprite doesn’t show up !!
Q2. My sprite has a background behind it!
Next → Day 5 - Backgrounds
— Mollusk 28/11/2005 00:35