gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Coding > (Zelda Heart Related Engine) *Solved* (Solution Inside)

#136245 - zaczac - Mon Jul 30, 2007 5:30 pm

Hey, im a little new to this forum, so dont flip out if im in the wrong place, im using DS kit 'Palib' and i thought maybe that something like this was achieved in GBA development. Anyway. I have a code
Code:
PA_StartSpriteAnim(1, 0, 0, 1, 1);

This sets a animation going on screen 1, sprite # 0, frame start, frame end and FPS.
I was wondering, if there would be a way to MANUALLY advance this sprites frame using Button presses? The reason being is to imagine a engine similiar to zelda's heart system. I have 4 frames (full, 3/4, half, 1/4, empty) and i want to change those frames when i want, using frames, instead of making new sprites (128 limit).

Has anyone made a similar engine and could reccommend something to solve this? I really would like to get it sorted out. I think maybe adding a %d + or - in there somewhere may do it, but if people can explain it a little, ill try it out and post my results.

Thanks for the help, id just really like to get this life meter engine done with.

:D


Last edited by zaczac on Thu Aug 02, 2007 5:35 pm; edited 1 time in total

#136252 - Lynx - Mon Jul 30, 2007 6:59 pm

Well, palib.com forums would probably be a better place to ask, I don't understand why you wouldn't just use PA_CreateSprite() instead? If it's not animated... Here's how I did it for LeoToddler:

PA_CreateSprite(0,0,(void*)AllLetters_Sprite, OBJ_SIZE_32X32, 1, 0, 46, 10);

Where:
0 = Screen
0 - 25 = Sprite Number for A - Z
(void*)AllLetters_Sprite = Sprite Name
OBJ_SIZE_32X32 = Sprite Size
1 = 256 Color Mode
0 = Sprite Pallette Number
46, 10 = X and Y Position on the screen.
_________________
NDS Homebrew Roms & Reviews

#136277 - zaczac - Mon Jul 30, 2007 10:06 pm

Well, it would therefore require something like 21 extra sprites for all the hearts on screen, and there is a 128 sprite limit (and id want bushes, monsters and other things on screen too)
I tried palib fourums, but no one replies. :(

#136306 - tepples - Tue Jul 31, 2007 3:15 am

If you make 8x32 or 32x8 pixel "four empty hearts" and "four full hearts" sprite cels, you may be able to make the status bar with fewer sprites.

Or you could devote a background layer to the status bar.

If you can draw a mock-up of your desired screen layout, save it as a PNG, and upload it to some picture host, one of us might be able to figure out an efficient way to divide the graphics between background layers and sprites.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#136320 - zaczac - Tue Jul 31, 2007 8:37 am

Thanks for the offer, but my idea isnt an exact copy of the heart system from zelda. What it is is a Sprite That Shows 10 empty 'Circles'. on frame 1. An Empty Circle appears in full, on frame 2...two circles appear full. This happens until the 10 empty circles are full..That saves 10 Sprites on one screen. Do you kinda get what im at here? Its more like "Alundras" Energy system really, but ididnt think people would recognize it. Anyway, i'd be glad for any information that you may have. I'll keep researching into it, but i'd really like to use as few sprites as possible.

Thanks ever so much for the input, i appriciate the kind offers.

#136333 - voteforpedro36 - Tue Jul 31, 2007 4:38 pm

PA_SetSpriteAnim should do what you want, that way you could use it like:

if (Pad.Newpress.Right) PA_SetSpriteAnim (1,0,2);

Where 1 is the screen, 0 is the sprite number, and 2 would be the third animation.

See the PAlib examples in Sprites->Animations->Frames.

#136334 - zaczac - Tue Jul 31, 2007 5:20 pm

Quote:
A_SetSpriteAnim should do what you want, that way you could use it like:

if (Pad.Newpress.Right) PA_SetSpriteAnim (1,0,2);

Where 1 is the screen, 0 is the sprite number, and 2 would be the third animation.

See the PAlib examples in Sprites->Animations->Frames.


No offense, but im not completely stupid.

Setting the sprites anim like that would require something like a variable 'life'
if(life == 1){
Pa_setspriteanim(0,0,0)
}
if(life == 2){
Pa_setspriteanim(0,1,1)
}
if(life == 3){
Pa_setspriteanim(0,2,2)
}

This would take forever, since the max life is a lot, I stated that i could do it in an easier way, but i was just asking for a more professional way of doing this. I supose i could use a switch statement (life) then case 0: etc etc but that still leaves streams of code.

I think that maybe i'll have to make do with a slapdash life engine for now, and then come back to it later, i supose as long as it works, it will be alright.

Thanks for the help guys, i appriciate it. Im still annoyed that i don't think using something like
Code:

for i(Something something){
Pa_SetspriteAnim(1,i,i)
}

Any Feedback if someone reckons that this would work? Im not too comfortable using expressions like these, confuse me a little. Maybe someone could reccomend a way / or code on how to use the above code correctly?

Thanks :D

#136521 - jenswa - Thu Aug 02, 2007 3:17 pm

Listen to what tepples says.

Just use the top part (8 or 16 pixels) of the background layer for your heart tiles. This way you get a space with different background heart tiles instead of sprites.

And in case of zelda like heart tiles, just draw one empty, one with a quarter, one with a half and one full heart.

And you need a little background code to show the right tiles, but that shouldn't be a problem. Just bind the hearts from empty to full with tilenumbers zero to three or one to four. Put them in a sequence in the right order, it makes things easier to code.
_________________
It seems this wasn't lost after all.

#136522 - phonymike - Thu Aug 02, 2007 3:58 pm

zaczac wrote:
Setting the sprites anim like that would require something like a variable 'life'
if(life == 1){
Pa_setspriteanim(0,0,0)
}
if(life == 2){
Pa_setspriteanim(0,1,1)
}
if(life == 3){
Pa_setspriteanim(0,2,2)
}

This would take forever, since the max life is a lot


I've never used PA_LIB, but this will do the same thing:
Code:
if(life >0){
 PA_setspriteanim(0, life-1, life-1);
}


easy huh.

#136528 - zaczac - Thu Aug 02, 2007 5:17 pm

Well i tried what you said. Seems logical
Code:
//Pad Controls The Life
if(Pad.Held.Down){
PA_SetSpriteAnim(1, lifediamond -= 1, lifediamond -= 1);
}


however, for SOME MAD reason.... the hud likes to fly about my screen!

EDIT
I changed it a little..
Code:
//Pad Controls The Life
if((Pad.Newpress.Right)&&(lifediamond >= 0)){
PA_StartSpriteAnim(1, 0, lifediamond -= 1, lifediamond -= 1, 1);
} //screen, frame start, frame en
if((Pad.Newpress.Left)&&(lifediamond <= 10)){
PA_StartSpriteAnim(1, 0, lifediamond += 1, lifediamond += 1, 1);
}


What this does is let you control the heart meter...but only for a short period and often gives garbage on the screen. Ill post back if i can get it sorted out.

Thanks a whole lot for the help 'phonymike', just the response i needed!


EDIT

I did it!
Code:
if((Pad.Newpress.Left)&&(lifediamond >= 0)){
PA_SetSpriteAnim(1, 0, lifediamond -= 1);
//PA_StartSpriteAnim(1, 0, lifediamond -= 1, lifediamond -= 0, 1);
}//screen, frame start, frame en
if((Pad.Newpress.Right)&&(lifediamond <= 10)){
PA_SetSpriteAnim(1, 0, lifediamond += 1);
}

if(lifediamond == 255){
lifediamond = 0;
PA_SetSpriteAnim(1, 0, 0);
}

if(lifediamond == 11){
lifediamond = 10;
PA_SetSpriteAnim(1, 0, 10);
}

PA_OutputText(1, 2, 18, "diamondlife : %d   ", lifediamond); //debug


Hurray! It works like a charm, quick...easy heart engine using only one sprite!

#136566 - phonymike - Thu Aug 02, 2007 9:46 pm

*smacks forehead

if you notice, I put "life - 1" not "life -= 1" as there's a BIG difference. but if it works for you then it's all good!

if you are gonna repeat the exact same thing many times, you'd be surprised how many times you can use the counting value in a loop. the code below isn't for gba stuff, but the heart of it draws 8 pixels in a row, and does that 8 times to make a tile, then 42 times to make the whole image. like 'tile_number' for instance. when it loops once, the next place to draw a tile is 40 bytes higher. so the first loop it'll write to 0x00. next time it loops it'll write to 0x40. next time it'll write to 0x80 and so on. from the simple counting value of 0, 1, 2 it also controls where data is written. it's horribly ugly :)

Code:
 for(tile_number=0;tile_number<42;tile_number++){
  for(tile_column=0;tile_column<8;tile_column++){
   for(tile_row=0;tile_row<8;tile_row++){
    player_image_snes[0x00 + (tile_column * 2) + (tile_number * 40)] |= ((player_image_bitmap[48*56 -48 + tile_row - (tile_column * 48) - (tile_number * 48)] & 1) >> 0) << (7 - tile_row);
    player_image_snes[0x01 + (tile_column * 2) + (tile_number * 40)] |= ((player_image_bitmap[48*56 -48 + tile_row - (tile_column * 48) - (tile_number * 48)] & 2) >> 1) << (7 - tile_row);
    player_image_snes[0x10 + (tile_column * 2) + (tile_number * 40)] |= ((player_image_bitmap[48*56 -48 + tile_row - (tile_column * 48) - (tile_number * 48)] & 4) >> 2) << (7 - tile_row);
    player_image_snes[0x11 + (tile_column * 2) + (tile_number * 40)] |= ((player_image_bitmap[48*56 -48 + tile_row - (tile_column * 48) - (tile_number * 48)] & 8) >> 3) << (7 - tile_row);
    player_image_snes[0x20 + tile_column       + (tile_number * 40)] |= ((player_image_bitmap[48*56 -48 + tile_row - (tile_column * 48) - (tile_number * 48)] &16) >> 4) << (7 - tile_row);
   }
  }
 }

#136582 - Cearn - Thu Aug 02, 2007 11:30 pm

phonymike wrote:
...
it's horribly ugly :)

But it doesn't need to be.

If you're using an expression multiple times, it's often more useful to put them in temporary variables beforehand instead of piling on more code. As far as I can tell, this does the same thing as your triple loop:
Code:
// NOTES
// * I'm assuming that "0x20 + tile_column" should have
//   had a *2 like the rest. If not, that complicates matters
//   a little.
// * I can't tell the pointer type, replace PTR with what it should be.

int ii, tx, ty;
PTR *dst, *dstBase= player_image_snes;
PTR *src, *srcBase= &player_image_bitmap[48*56-48];
u32 raw;

for(ii=0; ii<42; ii++)          // tile_number loop
{
    src= &srcBase[ii*48];
    dst= &dstBase[ii*40];

    for(ty=0; ty<8; ty++)       // tile_column loop
    {
        for(tx=0; tx<8; tx++)   // tile_row loop
        {
            raw= src[tx] << (7-tx);
            dst[0x00] |= (raw   ) & 1;
            dst[0x01] |= (raw>>1) & 1;
            dst[0x10] |= (raw>>2) & 1;
            dst[0x11] |= (raw>>3) & 1;
            dst[0x20] |= (raw>>4) & 1;
        }
        src += 48;
        dst += 2;
    }
}


Not only can you tell what actually happens now (though opinions on that may vary >_>), it's probably faster as well. Compilers are capable of doing some strength reduction and moving invariant code outside of the loop, if code gets that complex, it might just give up and recalculate everything every single time.

Global variables in particular have the habit of being reloaded; if used more than once (ok, maybe twice), assign globals to a local variable. And, of course, int/u32 for variables, not s8/u8 or u16/u32.

ETA: it's &srcBase[...] not jimply srcBase[...], silly


Last edited by Cearn on Fri Aug 03, 2007 8:41 am; edited 2 times in total

#136599 - phonymike - Fri Aug 03, 2007 3:01 am

yeah that looks a hell of a lot better! the code went together so quickly and I just want it to work. plus it's only gonna be ran a few times ever on a pc. it converts a bitmap into a snes format. wish I would have seen your post earlier cause my stuff would be a little neater. your code seems to be a little off, I don't blame ya cause of the mess you've looked at. it basically reads a .bmp (which is stored flipped vertically) and breaks it down into tiles. but, the bitmap stores all five bits like any other system, 000ABCDE. the 5bpp snes stores the bits in five different bytes. and yes that last byte is unaligned to the previous 4. the snes would store it as E0000000 D0000000 C0000000 B0000000 A0000000, addresses 0x00, 0x01, 0x10, 0x11, 0x20. the first four bytes are in groups of two, word aligned, while the last byte is byte aligned. silly but it saves space.

if you're curious what it all does:
[Images not permitted - Click here to view it]

I'm so tempted to rewrite it but seriously there's no point!

#138025 - elyk1212 - Sat Aug 18, 2007 7:31 pm

HAHA HA! Dude, that's great. [picture]

How did you know where toss your data to change the NBA Jam ROM? Did you just automatically know off hand where it was referencing data?