2D walk animation (is there any better way to do this?)

Started by
7 comments, last by julian boolean 16 years, 10 months ago
(Using Allegro/C++)

cAnimation::cAnimation()
{
	direction = NORTH;
        
	curframe = 0;    
	f_count = 0;
	f_delay = 10;
}

void cAnimation::animate_cycle()
{
	if (f_count++ > f_delay)
	{
		f_count = 0;

		switch (direction)
		{		
			case NORTH:
			{           
				curframe++;
				
				if (curframe < 0)
					curframe = 7;

				if (curframe > 7)
					curframe = 0;
			}break;

			case EAST:
			{
				curframe++;
				
				if (curframe < 8)
					curframe = 15;

				if (curframe > 15)
					curframe = 8;
			}break;

			case SOUTH:
			{
				curframe++;
				
				if (curframe < 16)
					curframe = 23;

				if (curframe > 23)
					curframe = 16;
			}break;
         
			case WEST:
			{
				curframe++;
				
				if (curframe < 24)
					curframe = 31;

				if (curframe > 31)
					curframe = 24;
			}break;
	
			case NORTHWEST:
			{           
				curframe++;
				
				if (curframe < 32)
					curframe = 39;

				if (curframe > 39)
					curframe = 32;
			}break;

			case NORTHEAST:
			{
				curframe++;
				
				if (curframe < 40)
					curframe = 47;

				if (curframe > 47)
					curframe = 40;
			}break;

			case SOUTHEAST:
			{
				curframe++;
				
				if (curframe < 48)
					curframe = 55;

				if (curframe > 55)
					curframe = 48;
			}break;
         
			case SOUTHWEST:
			{
				curframe++;
				
				if (curframe < 56)
					curframe = 63;

				if (curframe > 63)
					curframe = 56;
			}break;
		}
	}
}

void cAnimation::animate_half_cycle()
{
	if (f_count++ > f_delay)
	{
		f_count = 0;

		switch (direction)
		{		
			case NORTH:
			{           
				curframe++;
				
				if (curframe < 0)
					curframe = 3;
					
				if (curframe > 3)
					curframe = 0;
			}break;

			case EAST:
			{
				curframe++;
				
				if (curframe < 4)
					curframe = 7;
					
				if (curframe > 7)
					curframe = 4;
			}break;

			case SOUTH:
			{
				curframe++;
				
				if (curframe < 8)
					curframe = 11;
					
				if (curframe > 11)
					curframe = 8;
			}break;
         
			case WEST:
			{
				curframe++;
				
				if (curframe < 12)
					curframe = 15;
					
				if (curframe > 15)
					curframe = 12;
			}break;
	
			case NORTHWEST:
			{           
				curframe++;
				
				if (curframe < 16)
					curframe = 19;
					
				if (curframe > 19)
					curframe = 16;
			}break;

			case NORTHEAST:
			{
				curframe++;
				
				if (curframe < 20)
					curframe = 23;
					
				if (curframe > 23)
					curframe = 20;
			}break;

			case SOUTHEAST:
			{
				curframe++;
				
				if (curframe < 24)
					curframe = 27;
					
				if (curframe > 27)
					curframe = 24;
			}break;
         
			case SOUTHWEST:
			{
				curframe++;
				
				if (curframe < 28)
					curframe = 31;
					
				if (curframe > 31)
					curframe = 28;
			}break;
		}
	}
}

void cAnimation::animate_misc()
{
	if (f_count++ > f_delay)
	{
		f_count = 0;

		switch (direction)
		{		
			case NORTH:
			{           
				curframe++;
				
				if (curframe < 0)
					curframe = 3;
			}break;

			case EAST:
			{
				curframe++;
				
				if (curframe < 4)
					curframe = 7;
			}break;

			case SOUTH:
			{
				curframe++;
				
				if (curframe < 8)
					curframe = 11;
			}break;
         
			case WEST:
			{
				curframe++;
				
				if (curframe < 12)
					curframe = 15;
			}break;
	
			case NORTHWEST:
			{           
				curframe++;
				
				if (curframe < 16)
					curframe = 19;
			}break;

			case NORTHEAST:
			{
				curframe++;
				
				if (curframe < 20)
					curframe = 23;
			}break;

			case SOUTHEAST:
			{
				curframe++;
				
				if (curframe < 24)
					curframe = 27;
			}break;
         
			case SOUTHWEST:
			{
				curframe++;
				
				if (curframe < 28)
					curframe = 31;
			}break;
		}
	}
}
Basically, animate_cycle() would be used for walking or idling, so outside the class I could go:

void cPlayer::idle()
{
	draw_action = IDLE; // Drawing is done in a function elsewhere.
	animate_cycle();
}
Then animate_half_cycle() would be used for actions a player can do that doesn't use as many frames, like sitting or swimming. And finally, animate_misc() animates the attack of a player or monster, but it could also be used for other things, like maybe having an animation of drinking a potion or something. Any suggestions on how I could greatly improve this code?
Advertisement
(Using Allegro/C++)

cAnimation::cAnimation(){	direction = NORTH;        	curframe = 0;    	f_count = 0;	f_delay = 10;}



void cAnimation::animate_cycle(){	if (f_count++ &gt; f_delay)	{		f_count = 0;		switch (direction)		{					case NORTH:			{           				curframe++;								if (curframe &lt; 0)					curframe = 7;				if (curframe &gt; 7)					curframe = 0;			}break;			case EAST:			{				curframe++;								if (curframe &lt; 8)					curframe = 15;				if (curframe &gt; 15)					curframe = 8;			}break;			case SOUTH:			{				curframe++;								if (curframe &lt; 16)					curframe = 23;				if (curframe &gt; 23)					curframe = 16;			}break;         			case WEST:			{				curframe++;								if (curframe &lt; 24)					curframe = 31;				if (curframe &gt; 31)					curframe = 24;			}break;				case NORTHWEST:			{           				curframe++;								if (curframe &lt; 32)					curframe = 39;				if (curframe &gt; 39)					curframe = 32;			}break;			case NORTHEAST:			{				curframe++;								if (curframe &lt; 40)					curframe = 47;				if (curframe &gt; 47)					curframe = 40;			}break;			case SOUTHEAST:			{				curframe++;								if (curframe &lt; 48)					curframe = 55;				if (curframe &gt; 55)					curframe = 48;			}break;         			case SOUTHWEST:			{				curframe++;								if (curframe &lt; 56)					curframe = 63;				if (curframe &gt; 63)					curframe = 56;			}break;		}	}}


void cAnimation::animate_half_cycle(){	if (f_count++ &gt; f_delay)	{		f_count = 0;		switch (direction)		{					case NORTH:			{           				curframe++;								if (curframe &lt; 0)					curframe = 3;									if (curframe &gt; 3)					curframe = 0;			}break;			case EAST:			{				curframe++;								if (curframe &lt; 4)					curframe = 7;									if (curframe &gt; 7)					curframe = 4;			}break;			case SOUTH:			{				curframe++;								if (curframe &lt; 8)					curframe = 11;									if (curframe &gt; 11)					curframe = 8;			}break;         			case WEST:			{				curframe++;								if (curframe &lt; 12)					curframe = 15;									if (curframe &gt; 15)					curframe = 12;			}break;				case NORTHWEST:			{           				curframe++;								if (curframe &lt; 16)					curframe = 19;									if (curframe &gt; 19)					curframe = 16;			}break;			case NORTHEAST:			{				curframe++;								if (curframe &lt; 20)					curframe = 23;									if (curframe &gt; 23)					curframe = 20;			}break;			case SOUTHEAST:			{				curframe++;								if (curframe &lt; 24)					curframe = 27;									if (curframe &gt; 27)					curframe = 24;			}break;         			case SOUTHWEST:			{				curframe++;								if (curframe &lt; 28)					curframe = 31;									if (curframe &gt; 31)					curframe = 28;			}break;		}	}}


void cAnimation::animate_misc(){	if (f_count++ &gt; f_delay)	{		f_count = 0;		switch (direction)		{					case NORTH:			{           				curframe++;								if (curframe &lt; 0)					curframe = 3;			}break;			case EAST:			{				curframe++;								if (curframe &lt; 4)					curframe = 7;			}break;			case SOUTH:			{				curframe++;								if (curframe &lt; 8)					curframe = 11;			}break;         			case WEST:			{				curframe++;								if (curframe &lt; 12)					curframe = 15;			}break;				case NORTHWEST:			{           				curframe++;								if (curframe &lt; 16)					curframe = 19;			}break;			case NORTHEAST:			{				curframe++;								if (curframe &lt; 20)					curframe = 23;			}break;			case SOUTHEAST:			{				curframe++;								if (curframe &lt; 24)					curframe = 27;			}break;         			case SOUTHWEST:			{				curframe++;								if (curframe &lt; 28)					curframe = 31;			}break;		}	}}


Basically, animate_cycle() would be used for walking or idling, so outside the class I could go:

void cPlayer::idle(){	draw_action = IDLE; // Drawing is done in a function elsewhere.	animate_cycle();}


Then animate_half_cycle() would be used for actions a player can do that doesn't use as many frames, like sitting or swimming. And finally, animate_misc() animates the attack of a player or monster, but it could also be used for other things, like maybe having an animation of drinking a potion or something.

Any suggestions on how I could greatly improve this?

Beginner in Game Development?  Read here. And read here.

 

(Using Allegro/C++)

cAnimation::cAnimation(){	direction = NORTH;        	curframe = 0;    	f_count = 0;	f_delay = 10;}



void cAnimation::animate_cycle(){	if (f_count++ &gt; f_delay)	{		f_count = 0;		switch (direction)		{					case NORTH:			{           				curframe++;								if (curframe &lt; 0)					curframe = 7;				if (curframe &gt; 7)					curframe = 0;			}break;			case EAST:			{				curframe++;								if (curframe &lt; 8)					curframe = 15;				if (curframe &gt; 15)					curframe = 8;			}break;			case SOUTH:			{				curframe++;								if (curframe &lt; 16)					curframe = 23;				if (curframe &gt; 23)					curframe = 16;			}break;         			case WEST:			{				curframe++;								if (curframe &lt; 24)					curframe = 31;				if (curframe &gt; 31)					curframe = 24;			}break;				case NORTHWEST:			{           				curframe++;								if (curframe &lt; 32)					curframe = 39;				if (curframe &gt; 39)					curframe = 32;			}break;			case NORTHEAST:			{				curframe++;								if (curframe &lt; 40)					curframe = 47;				if (curframe &gt; 47)					curframe = 40;			}break;			case SOUTHEAST:			{				curframe++;								if (curframe &lt; 48)					curframe = 55;				if (curframe &gt; 55)					curframe = 48;			}break;         			case SOUTHWEST:			{				curframe++;								if (curframe &lt; 56)					curframe = 63;				if (curframe &gt; 63)					curframe = 56;			}break;		}	}}


void cAnimation::animate_half_cycle(){	if (f_count++ &gt; f_delay)	{		f_count = 0;		switch (direction)		{					case NORTH:			{           				curframe++;								if (curframe &lt; 0)					curframe = 3;									if (curframe &gt; 3)					curframe = 0;			}break;			case EAST:			{				curframe++;								if (curframe &lt; 4)					curframe = 7;									if (curframe &gt; 7)					curframe = 4;			}break;			case SOUTH:			{				curframe++;								if (curframe &lt; 8)					curframe = 11;									if (curframe &gt; 11)					curframe = 8;			}break;         			case WEST:			{				curframe++;								if (curframe &lt; 12)					curframe = 15;									if (curframe &gt; 15)					curframe = 12;			}break;				case NORTHWEST:			{           				curframe++;								if (curframe &lt; 16)					curframe = 19;									if (curframe &gt; 19)					curframe = 16;			}break;			case NORTHEAST:			{				curframe++;								if (curframe &lt; 20)					curframe = 23;									if (curframe &gt; 23)					curframe = 20;			}break;			case SOUTHEAST:			{				curframe++;								if (curframe &lt; 24)					curframe = 27;									if (curframe &gt; 27)					curframe = 24;			}break;         			case SOUTHWEST:			{				curframe++;								if (curframe &lt; 28)					curframe = 31;									if (curframe &gt; 31)					curframe = 28;			}break;		}	}}


void cAnimation::animate_misc(){	if (f_count++ &gt; f_delay)	{		f_count = 0;		switch (direction)		{					case NORTH:			{           				curframe++;								if (curframe &lt; 0)					curframe = 3;			}break;			case EAST:			{				curframe++;								if (curframe &lt; 4)					curframe = 7;			}break;			case SOUTH:			{				curframe++;								if (curframe &lt; 8)					curframe = 11;			}break;         			case WEST:			{				curframe++;								if (curframe &lt; 12)					curframe = 15;			}break;				case NORTHWEST:			{           				curframe++;								if (curframe &lt; 16)					curframe = 19;			}break;			case NORTHEAST:			{				curframe++;								if (curframe &lt; 20)					curframe = 23;			}break;			case SOUTHEAST:			{				curframe++;								if (curframe &lt; 24)					curframe = 27;			}break;         			case SOUTHWEST:			{				curframe++;								if (curframe &lt; 28)					curframe = 31;			}break;		}	}}


Basically, animate_cycle() would be used for walking or idling, so outside the class I could go:

void cPlayer::idle(){	draw_action = IDLE; // Drawing is done in a function elsewhere.	animate_cycle();}


Then animate_half_cycle() would be used for actions a player can do that doesn't use as many frames, like sitting or swimming. And finally, animate_misc() animates the attack of a player or monster, but it could also be used for other things, like maybe having an animation of drinking a potion or something.

Any suggestions on how I could greatly improve this?

Beginner in Game Development?  Read here. And read here.

 

Double post ;) I was referring to getting rid of the switches and having maybe just one function, if thats even possible.
When I was trying to learn some SDL (and practice some OOP concepts simultaneously), I came up with an idea for an animation class that used a dynamic array (vector) of frames and constantly looped through them. That way I could add or remove specific frames from a certain animation as I saw fit.

This allowed me to do stuff like this:
Animation* Walk_North = new Animation();Walk_North.loopable = true;Walk_North.AddFrame(Frame01);Walk_North.AddFrame(Frame02);Walk_North.AddFrame(Frame03);Walk_North.AddFrame(Frame04);Animation* Walk_East = new Animation();Walk_East.loopable = true;Walk_East.AddFrame(Frame05);Walk_East.AddFrame(Frame06);Walk_East.AddFrame(Frame07);Walk_East.AddFrame(Frame08);


...and so on.

Ultimately, each "Animation" could be loaded into a list, inside a "Sprite" class. Then, depending on the Sprite's 'state' (north, east, etc), it would find the appropriate animation in the list and loop through it. Perhaps it could be used in this fashion...
Sprite* JohnDoe = new Sprite();JohnDoe.AddState("Walk North", Walk_North);JohnDoe.AddState("Walk East", Walk_East);



Then, when you're ready to show the sprite on the screen, you need only tell the object what it's current state is, and it will automatically handle the correct frame to show. Maybe something like:
JohnDoe.SetCurrentState("Walk East");//This would return the current frame of the character's animationCurrentFrame = JohnDoe.ShowCurrentFrame();//Update the sprite (i.e. move to the next frame if appropriate)JohnDoe.Update();


The way you have it set up now, in order to add a frame or two to your walking animation, you'd have to go back and re-code all the frame numbers in your source. Using the above approach, you'd only have to run the AddFrame() function a few more times. It also allows for different characters to have different-length walking animations (for instance, a tall character with a longer stride might have more frames in his animation than a short character who shuffles subtly).
Deep Blue Wave - Brian's Dev Blog.
That seems like an interesting way of doing it, but I don't think it would fit well with the way most of my code is right now. Going to keep looking it over though, thank you. :)

Can anyone tell me if the code I posted previously is acceptable for something that's not trivial? If no, then please give me some more input! ;)

Edit:

On second though, what might your animation class look like?

[Edited by - julian boolean on May 24, 2007 8:13:29 PM]
As far as I can tell, your code looks fine. The top one is (obviously) easier to read, however, as it doesn't contain as much shorthand. As far as using it for something non-trivial: like I said, if you decided to change the number of frames in any given animation, you'd have to go back and re-code all the numbers again. It's very unflexible. But it'd work, and sometimes that's more important - you can always go back and change the inner-workings later as long as the end result is the same (ah, the beauty of OOP).

For what I would do with the animation class I proposed above: I haven't actually programmed one out yet, but here's a spitball-version. There's other stuff you can add, like a bool for whether or not the animation should be loopable.

class Animation{     private:          //this is the "filmstrip": you add frames to it, and pair each frame          //with a delay before the next frame is shown (in milliseconds)          vector<Pair<Frame, int>> strip;          //this points to the current frame in the strip.          Iterator<vector<Pair<Frame, int>>> currentFrame;     public:          //Obvious stuff goes here:          //Constructor, etc.          void Update(); //calculates how much time has passed since the last                           //frame was shown; move on to next frame if needed.          Frame GetCurrentFrame(); //returns the current frame in the animation.                    void AddFrame(Frame newFrame); //adds another frame to the animation          void Restart();  //restarts the animation from the beginning};


Don't expect this to be a well-planned class; like I said, this is a general idea. Subsequently, you'd have to design a Sprite class, that would contain a list of animations that correspond to states of the sprite
Map<CharacterState, Animation> animations;
(where CharacterState is an enum).
Deep Blue Wave - Brian's Dev Blog.
Well, you could get rid of all those cases if you did something like:

Enumerate your directions: North = 0, EAST = 1, SOUTH = 2...

Then, when you know what direction your character is walking do something like:

initialFrame = direction * NUMBER_OF_FRAMES_IN_THE_WALKING_ANIMATION;
finalFrame = currentFrame + NUMBER_OF_FRAMES_IN_THE_WALKING_ANIMATION - 1;
(in you code that constant would be 8)

You see what we are doing there? We are setting the beginning and end of the animation just by knowing its value... that is, "0" will give us 0 and 7. "1" will give us 8 and 15...

and when you have to change frames you just do the same for every animation, no "cases" involved:

currentFrame++;
if(currentFrame > finalFrame)
currentFrame = initialFrame;


Hope that helps!
As the McDonald's slogan goes, "I'm lovin' it."

Your ideas are great and I'm going to look into it more throughly tonight. Big thanks to everyone for the input, but I'm *always* open for more even though I think this is more then enough. Thanks again. :)

EDIT:

How would that work with "if (key[KEY_UP])", etc?

EDIT AGAIN:

I should also mention I'm using this function to draw the frames from a sprite sheet:

void drawframe(BITMAP* source, BITMAP* dest, int x, int y, int width, int height, int startx, int starty, int columns, int frame){	int framex = startx + (frame % columns) * width;	int framey = starty + (frame / columns) * height;	masked_blit(source, dest, framex, framey, width, height);}


[Edited by - julian boolean on May 25, 2007 5:23:39 PM]

This topic is closed to new replies.

Advertisement