#169360 - mog123 - Mon Jul 06, 2009 12:21 pm
Hey, I've got a question how to do you organize your code in GBA games. Say you have an rpg game and you: divide the certain blocks into .c files like (intro.c menu.c game_engine.c etc) and in your main.c just do something like:
Code: |
while(1){
intro();
if(KEY_PRESS(START)) menu();
...etc? |
Another thing, how do you organize sprites in VRAM/add them to the screen.
Let's say I have a 16x32p sprite that is 2x4t. That's a sprite of a guy standing. Let's say I want to just change his legs moving, not the whole guy, So do I have to copy a whole new 16x32 sprite (another head + body with only legs moving) and change his tid OR should I add the sprites in 16x16p chunks and manage the legs spearately from the rest of the body? How do you guys do it?
Also, If you have the spare time, can anyone give me a short algorythm how to animate sprites ? Do I need to use counters or can I update the tid every other frame and flip it with if(frame%4) or sth like that?
Thanks a bunch!
#169362 - DekuTree64 - Mon Jul 06, 2009 2:34 pm
About organization: I usually have a GameMode.c or something to that effect, which manages switching between intro, world map, main game, menus, etc., so my Main.c looks like this:
Code: |
[init lots of stuff]
GameModeInit();
while(1) {
UpdateGameMode();
WaitForVBlank();
} |
It's pretty simple. Either a function pointer table or a switch statement on an enum of game modes. Each mode has an init, update, and exit function. Then GameMode has a variable for the current mode, and a variable for a new mode to switch to. You can set the new mode variable at any point, and then at the start of the next frame it checks if it's not set to some "no new mode" value and then calls the current mode's exit, new mode's init, and sets the current mode to the new mode.
About sprites: I wouldn't bother updating the head and legs separately unless I had good reason. Some games use separate sprites for body and legs so you can mix and match upper body animations with standing, walking, crouching, etc. on the legs. But if the only reason the head doesn't change is that I was too lazy to add a little movement in the animation, then it's not worth the trouble.
About animations: I usually use a delay counter so I can have different delay for each frame of the animation. It also takes less CPU cycles to update than a mod. But unless you have 50 sprites on screen, I wouldn't worry about it. So if you don't need per-frame delay, either way is fine.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku
#169363 - mog123 - Mon Jul 06, 2009 2:50 pm
Thanks for the detailed post. It really sorted some stuff up in my head.
About sprite animation: If I'll try to add so much sprites and animate them, I'll run out of OAM in a second. I've got a 16x32 main character that's going to walk in 8 directions with about 5 frames for each direction + he's gonna attack, cast spells etc. SO just the main character will eat up all my OAM. How do all the games handle it then?
#169364 - DekuTree64 - Mon Jul 06, 2009 3:26 pm
Well, first of all, OAM is the sprite position/priority/tile index/palette index/etc. definitions. I think what you mean is VRAM (the actual pixel data).
The trick to it is that you only need VRAM for what's displayed on the screen for the current frame. So your player character really only needs enough VRAM for a single 16x32 frame, and then you can copy new pixel data into it from ROM whenever his animation frame changes.
Copying pixel data around all the time of course takes more CPU cycles than just changing what VRAM location the sprite points to, but usually there's just not enough VRAM otherwise. You can combine techniques though, if you have plenty of VRAM but run into speed problems. Maybe keep idle and walking frames in VRAM all the time, but dynamically load the more rarely used ones. Probably not necessary for an RPG though.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku
#169365 - mog123 - Mon Jul 06, 2009 4:13 pm
Yes, I finally understand now :)
Thank you so much. I'm gonna try to write my own copying/animation function on days, so please keep track of this topic to see and grade it when I'm finally done.
#169379 - mog123 - Tue Jul 07, 2009 1:37 pm
Hey, I did the loading function :
Code: |
void oam_framecpy(u32 sprite_number,u32 frame, u32 sprite_size)
{
memcpy(&tile_mem[4][sprite_number*16], &spritesTiles[frame*256], sprite_size);
} |
The only downside is That it'll work only if by the road there won't be any sprites different in size than 256 (16*32 or 32*16 or 8*64 etc.)
Is there a "cure" for this? The only one I can think of is keeping all spritesheets in separate files.
Oh, I almost forgot, how to set a color to be transparent? I know I have to change it's value to 0x0000, but how to do it with code?
Thanks!
#169388 - zazery - Wed Jul 08, 2009 6:10 am
mog123 wrote: |
The only downside is That it'll work only if by the road there won't be any sprites different in size than 256 (16*32 or 32*16 or 8*64 etc.)
Is there a "cure" for this? The only one I can think of is keeping all spritesheets in separate files. |
There are many possible solutions to the problem. One of them is to write a dynamic memory allocator for VRAM. Keep track of where contiguous blocks of VRAM are free and pick one that will fit the sprite memory you're trying to allocate. I don't think external fragmentation will be a problem since as you pointed out many sprite shapes have the same tile count.
mog123 wrote: |
Oh, I almost forgot, how to set a color to be transparent? I know I have to change it's value to 0x0000, but how to do it with code?
Thanks! |
The first index of a palette is the transparent colour regardless of the colour. Commonly the colour is magenta or cyan because those colours are rarely used. See LCD Colour Palettes on GBATek for more information.
#169419 - mog123 - Thu Jul 09, 2009 4:57 pm
Ok, here's my walker demo:
http://rapidshare.com/files/253846360/walkerguy.zip.html
I tried changing the first color in the palette to 0x7C1F(the magenda I'm using) but it didn't work, I Still saw the magendish border around the sprite, it wasn't transparent (black)
I did a simple end animation detection so it loads a still image after the animation finishes. The walker walks in place, so I could see if I placed the frames in correct position, but you can change that by changing the speed variable. Any thoughts on my code? Please give me some advice m(_ _)m
#169422 - zazery - Thu Jul 09, 2009 7:03 pm
mog123 wrote: |
I tried changing the first color in the palette to 0x7C1F(the magenda I'm using) but it didn't work, I Still saw the magendish border around the sprite, it wasn't transparent (black) |
The reason for the magenta background being displayed is it is not the first colour of the palette. Open up the Palette Viewer in VisualBoyAdvance and you will see that magenta is the 253rd colour of the palette. You can use Usenti to reorder the palette. The first colour of the palette is currently set to black which means black is your transparent colour right now. Black is not a default transparent colour but certainly looks like it if your background solid black as well.
If you want to use black as part of your sprite then you'll need to have two black palette entries which makes editing a challenge because you cannot distinguish between the two. This is why people use magenta as a transparent colour since it is almost never used in an actual sprite.
#169423 - mog123 - Thu Jul 09, 2009 7:54 pm
Ok then, I'll try to do it, how does the code look to you?
#169634 - Karatorian - Mon Jul 27, 2009 4:58 pm
Generally, I organize my game code as a state machine. Each part of the game is a state. For instance, the intro is a state, the various menus are each a state, the main gameplay part is a state (or several states, depending on the game), pause is a state, game over is a state, etc.
Each state has it's own input handling, game logic, and rending code. Which one the main loop calls depends on what state the game is currently in. Of course, by it's own input handling and other code, I don't mean that there's pointless code duplication all over the place. Only the part that has to be different is changed.
For instance, in a menu state, pressing the A button could select a menu item, while in the game proper it could shoot or something. The part that reads the keypad is the same (in fact, it's in the main loop). However, once the keypad has been read, the state specific input handler decides what to do about it.
Rendering works similarly. My main graphics routine runs every loop, but the data it's rendering has been selected by the game logic code specific to the current game state.
Using a state machine is a great convience. It allows you to cleanly modularize your codebase while reusing the bits that ought to remain fairly consistant. It also keeps your code relatively free of complex and error prone if chains or switches.