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.

Beginners > how to slow down frames (2 part question)

#90336 - Chucky - Thu Jun 29, 2006 7:31 pm

PART I:
Hello, I am using a method to animate this sprite I have that seems kind of easy and works really well (it just checks if the position of the sprite is divisible by 5 (in terms of X coordinate) and if it is, loads another drawing of the sprite, giving it the feeling that the guy is running (i know it's not good to use the % function but it seems to run very well... my other option was to check if the Xcoordinate was an even number and load the other animation this way, but I feel this was animating too fast).

Code:
   memcpy( (u16 *)0x06014000, &main_00Data, sizeof(main_00Data) ); //guy going RIGHT (1st animation)
   memcpy( (u16 *)0x06014400, &main_01Data, sizeof(main_01Data) ); //guy going RIGHT (2nd animation) (512+32)
   
   Sprite[0].attr0 = OBJ_256_COLOR | OBJ_SQUARE | 108;
   Sprite[0].attr1 = OBJ_SIZE(2) | 40;  //OBJ_SIZE(2) means 32x32 (it knows it has to be square because of OBJ_SQUARE in attr0)
   Sprite[0].attr2 = 512;
//..................
   if(KeyDown(KEY_LEFT) && xmain_00 > xMin)
      {
         if((xmain_00 % 5 ) == 0)
            {Sprite[0].attr1 = OBJ_HFLIP | OBJ_SIZE(2); Sprite[0].attr2 = 512;} //
         else
            {Sprite[0].attr1 = OBJ_HFLIP | OBJ_SIZE(2); Sprite[0].attr2=512+32;} //
      xmain_00--;xmain_00--;
      }

what this will do is if i hit RIGHT (code not shown) the attr1 OBJ_HFLIP is not in there (therefore he will be facing RIGHT), it also checks if his x coordinate is divisible by 5, and if it is, will load the 2nd animation (as you see i did not have to declare the Sprite[1] info, I just use Sprite[0] and the other stored info on the second sprite (and OBJ_HFLIP) so instead of using 4 different sprites, I am only using Sprite[0]. So when i hit LEFT it does use the OBJ_HFLIP to make it seem like he has turned.

I just thought I would show this to get some feedback, if this is a good way to do the animation of him just running or if there is a nicer way (such as checking if his xcoordinate is even or odd, which i found to be kind of fast).

PART 2:
So, then I moved on to jumping animation, what I'm doing basically is taking the top left coordinate of my sprite (at 108) and I have a loop setup for the yvector array, so that it adds that value to the coordinate (no fancy equation just a simple jump motion will suffice). Here is the relevant code:

Code:
u16 yvector[6] = {-15,-30,-39,-30,-15,0};
u8 j_height = 40;
//........................
   if(KeyDown(KEY_A) && ymain_00 > (yMax - j_height))
      {
      u16 i;
         for(i = 0; i < 6 ; i++)
            {ymain_00 = yMax + yvector[i];}
      }


so basically what it does is update ymain_00 (this is the location on the Y axis of the sprite) by adding those values to it... the problem is, it is doing it really fast and either my code is garbage OR it's doing it properly but so fast that I can't detect the motion.

I am wondering what is a good way to allow the game to understand that I need just this part slowed down? Would it make sense to increase the yvector array to go from {-1,-2,..*max*..,1,0}? or is there a fancier way that may end up making the rest of my life easier (later on when I start doing better things)

Thanks again in advance!

#90471 - Chucky - Fri Jun 30, 2006 10:55 am

I have been doing some reading on the TONC tutorial, and there are built in GBA timers (0,1,2,3) I saw from gba_timers.h file that came with libgba things such as TIMER_START which i'm guessing starts the timer, the thing i'm having difficulties with is getting it to perform the loop which calculated the sprites position at each specific point in the timer (like 0.4 second intervals at which this equation is calculated... or if 0.4 is not a good number, something easier on the GBA). Any help with sample code or links to some would be really appreciated.

Thanks again in advance!

*PS* Anyone see the animation code I have put earlier? Is it a good method or is there a better way?

#90530 - DekuTree64 - Fri Jun 30, 2006 5:22 pm

The best method of animation I know is using delay counters, that count once per VBlank:
Code:
struct Sprite
{
    u8 frame;
    u8 numFrames;
    u8 frameDelay;
    u8 delayCounter;
};

void UpdateAnimation(Sprite *sprite)
{
    if (sprite->frameDelay != 0) // Use 0 as non-animating
    {
        if (++sprite->delayCounter >= sprite->frameDelay)
        {
            if (++sprite->frame >= sprite->numFrames)
                sprite->frame = 0;

            // Do like your setting of the OAM char start
            SetFrame(sprite->frame);

            sprite->delayCounter = 0;
        }
    }
}

So then all you have to do is call UpdateAnimation on everyone in your main loop. VBlank happens 60 times per second, so assuming your main loop is something like
Code:
while(1)
{
    Update();
    WaitForVBlank();
}

and Update doesn't take more than 1/60 of a second to run, then setting the sprite's frameDelay to 10 will run at 6 frames per second.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#90808 - Edelnutte - Sun Jul 02, 2006 6:21 pm

I'm pretty new to programming so I dont know if it's possible what I say.

You could make a Counter that runs along with the VBlank and gets back to zero if a certain value is reached. You could animate with the difference of the Counter and since you know how long Vblank takes it would be an ease.
Unfortunateley I dont know how much processor power or whatever This will take

#91509 - thegamefreak0134 - Fri Jul 07, 2006 5:34 pm

Quote:
You could make a Counter that runs along with the VBlank and gets back to zero if a certain value is reached. You could animate with the difference of the Counter and since you know how long Vblank takes it would be an ease.
Unfortunateley I dont know how much processor power or whatever This will take


Um... That's what DekuTree64 just said, basically.

Although i don't think it would work for this project, I have a states system going in my game that allows for all kinds of animation. The physics of the character are separate from the frames of the animation, and associated with the various "states" the character can be in. (running, jumping, eating cheese, etc.) Then, every state has a list of single bytes that correspond to the frames of the animation for that state. (A separate function updates the frames accordingly, but it could also all be tied to an interrupt I suppose.) As the state runs, so do the physics and checks and such for that state. When the state reaches an end, it either jumps to another state or "loops" by jumping to itself.

This is a pretty common way of doing things, I believe. However, it may be a bit too much for simple animation.

-gamefreak
_________________
What if the hokey-pokey really is what it's all about?

[url=http:/www.darknovagames.com/index.php?action=recruit&clanid=1]Support Zeta on DarkNova![/url]

#91971 - Chucky - Mon Jul 10, 2006 9:41 pm

well I have been trying some things, with timers and basically checking if the timer is divisible by a certain number, to execute the code. The problem is sometimes it will not do the complete motion, like all the values in the array will not be calculated, so he will end up in midair or something.

I was reading some here and someone said doing an Asteroids clone or something similar would help in understanding the timing, would anyone have link to the tutorial on doing this?

Thanks again in advance.

#92025 - ScottLininger - Tue Jul 11, 2006 4:49 am

Okay, so here's step one: get it working in a totally simple manner first, then you can work on a more elegant solution. I recommend "slowing down" the loop you're in... (see below.)

(This won't work for an actual game, since the rest of your game will pause while you animate your one sprite, but at least you can see if it's changing. Your code is structured in such a way that the entire "jump" happens "between" the rest of the action anyway, so eventually you'll need to do something more like DekuTree said...)

Another important question, are you updating the OAM during VBlank in your program? There's no line of code in there to do so... Thought maybe you're doing it inside an interrupt?


Code:

// the old Pern Project function for slowing stuff down...
// it just eats up some cycles, "pausing" the game
void Sleep(int i)
{
   int x, y;
   int c;
   for (y = 0; y < i; y++)
   {
      for (x = 0; x < 20000; x++)
         c = c + 2; // do something to slow things down
   }
}

u16 yvector[6] = {-15,-30,-39,-30,-15,0};
u8 j_height = 40;
//........................
if(KeyDown(KEY_A) && ymain_00 > (yMax - j_height))
{
     u16 i;
     for(i = 0; i < 6 ; i++)
    {       
          ymain_00 = yMax + yvector[i];
          WaitFoVBlank();
          UpdateOAM(); // or whatever your function is called
          Sleep(100); // change the number to change the pause
     }
}




Hope that helps.

-Scott