#70246 - gs2phateon - Sun Feb 05, 2006 3:36 am
I'm about to start sprite animation for a project I'm working on, but I need some help to get started. There are two ways I can think of doing animation.
First, I could just draw out every sprite for every animation, and have the program pick which frame to use out of every availible picture. This would let me save memory by re-using frames.
Or, I could put all the frames in the right order in each file I use. I imagine this would be easier to program for (I don't have to worry about using the wrong frame accidentally), but it's not as efficient and I would probably use several frames over and over when I don't necessarily have to.
Right now I'm leaning towards the second option (just because it's easier to program), but do animation frames take up enough space that I should be cautious as to how many I make? Or is there a better way to animate a sprite?
#70250 - tepples - Sun Feb 05, 2006 3:47 am
For a 32x32 pixel sprite, each sprite cel takes 0.5 KB if not compressed. How many sprite cels did you plan to have in the game?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#70252 - gs2phateon - Sun Feb 05, 2006 4:05 am
Right now I'm not sure, but I don't want to underestimate. This project will probably use more sprites then the average game, that's all I know. I'm guessing without including enemy sprites, I could have 75 individual frames for the main sprite, if I don't conserve them.
#70255 - tepples - Sun Feb 05, 2006 4:14 am
75 frames isn't a lot at all; that's just 37.5 KB out of a 4096 KB ROM. Are you making your game for a ROM or for a multiboot?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#70259 - gs2phateon - Sun Feb 05, 2006 4:21 am
You're right, I added up KB total. This will be for a ROM, so I guess I'll be just fine. But is the second way I suggested (using all the sprites in a file according to the exact order they will be used) the best way to do animation?
#70416 - gauauu - Mon Feb 06, 2006 8:12 am
The "best" depends on what sorts of animation will occur (will all frames be in same order every time?)
What I do is define an animation structure that includes a pointer to a frame, the amount of time that frame should display, and a pointer to the next animation structure. This provides all sorts of flexibility. Each character might have multiple animation sequences, some of which automatically flow into other ones.
Then, I can also programmatically tell it to jump to any animation I want, like:
setCharacterToAnimation(character, animationFrame);
#70519 - Touchstone - Tue Feb 07, 2006 12:19 am
If you're really serious about your animations and want to have big animated characters (or whatever's animated) you could do cut out style animation, like South Park. I started writing a library for this but I never finished it. (Because I'm to lazy) Anyways, check out this stuff http://www.pokewhat.com/touchstone/
If you're interested I can send you the source. It's quite pretty, I used it as a code sample for when I was looking for a job. Unfortunatelly it's not very dynamic yet, you have to keep track of where in VRAM you load your sprite tiles etc, but that's an easy fix if you have your VRAM allocation scheme already set up. My problem was that I was trying to be too dynamic and in the end I got nothing done.
_________________
You can't beat our meat
#70521 - gs2phateon - Tue Feb 07, 2006 12:31 am
Could you explain the structure animation style some more, I'm not sure I understand the whole thing yet. But that sounds like what I'm going for. I will be using a wide variety of animations, and I need to be able to call them quickly and easily, sort of like in a fighting game.
#70531 - gauauu - Tue Feb 07, 2006 2:50 am
Ok, here's how I do it. Keep in mind, I'm no expert...there's probably a much better way, but this works for me.
Basically, there's 3 chunks of data that I have for each character's animation.
First is the sprite tile data itself.
Second is what I call the Frame Definition, which defines which tiles to use how to make one frame of animation (for example, large characters might be a mix of all sorts of combinations of smaller sprites)
Third is what I call the Animation Definition, which defines which frames to use when.
So first is the structure for Frame Definitions. Basically, each frame definition is a linked list of sprite definitions. The code follows the nextSprite pointer (as long as it isn't null) to find the next sprite in the character frame:
Code: |
struct characterFrameDefStruct
{
int xOffset; //where the sprite appears relative
int yOffset; //to the character's origin
int tileNum; //which tile index for the sprite
int shape; //sprite attributes. you could add vflip, etc
int size; //if you planned to use them
bool hflip;
struct characterFrameDefStruct * nextSprite; //the next sprite in the frame
};
//I like to typedef structs. There are pros and cons. Do a forum search
//to read everyone's opinions.
typedef struct characterFrameDefStruct characterFrameDef;
|
So my character data files usually have a chunk that looks like this:
Code: |
#define POS1 -8,-8
#define POS2 8,-8
#define MCFrame(x) (characterFrameDef *)&(mainCharFrames[x])
const characterFrameDef mainCharFrames[] =
{
////FRAME: north 1
{//0
POS1, //x,y offset
10, //tile
TALL, //shape (SQUARE/TALL/WIDE)
SIZE_32, //size
false, //flip
MCFrame(1) //nextSprite
},
{//1
POS2, //x,y offset
12, //tile
TALL, //shape (SQUARE/TALL/WIDE)
SIZE_16, //size
false,
null //nextSprite
},
//FRAME: north 2
{//2
POS1, //x,y offset
13, //tile
TALL, //shape (SQUARE/TALL/WIDE)
SIZE_32, //size
false, //flip
MCFrame(3)
},
{//3
POS2, //x,y offset
15, //tile
TALL, //shape (SQUARE/TALL/WIDE)
SIZE_16, //size
false,
null //nextSprite
},
...etc
|
Eventually, I'd like to create a tool to generate this data automatically, instead of having it typed into C files. But oh well.
As you can see, this defines 2 frames of animation, each using 2 sprites. The POS1, TALL, SIZE_16, etc, are all #defined values.
Now that we have the frames defined for a character, we have to define the animation sequences. I use a very similar system for animation as I do for defining the frames. A linked list of definitions, each to refer to a frame, and point to the next frame:
Code: |
struct characterAnimDefStruct
{
int frameDelay; //how long to show the frame
characterFrameDef * thisFrame; //pointer to frame definition for this
//frame
struct characterAnimDefStruct * nextFrame; //next frame in this animation
//sequence
};
typedef struct characterAnimDefStruct characterAnimDef;
|
So then a typical definition might look like:
Code: |
#define MCAnim(x) (void *)&(mainCharAnim[x])
#define WALK_SPEED 10
#define ATTACK_SPEED 4
#define REST_SPEED 13
const characterAnimDef mainCharAnim[] =
{
{//0 - north frame 1
WALK_SPEED,
MCFrame(0),
MCAnim(1)
},
{//1 - north frame 2
WALK_SPEED,
MCFrame(2),
MCAnim(2)
},
{//2 - north frame 3
WALK_SPEED,
MCFrame(0),
MCAnim(3)
},
{//3 - north frame 4
WALK_SPEED,
MCFrame(4),
MCAnim(0)
},
....etc
|
As you can see here, sometimes a frame can be used in multiple places in one animation sequence (like a "neutral" position when walking). Another nice thing about this is that some other special animations flow into other defined animations. For example, if I have a resting animation, where my character shifts his weight back and forth, then you can define that the attack animation always flows into the resting animation loop.
Now, in my character's code, to set which animation to start, I do something like:
Code: |
#define mainCharAnimWalkN mainCharAnim
#define mainCharAnimWalkS (characterAnimDef*)&mainCharAnim[11]
#define mainCharAnimWalkW (characterAnimDef*)&mainCharAnim[19]
#define mainCharAnimWalkE (characterAnimDef*)&mainCharAnim[27]
setCharacterAnimationForced(mainChar, mainCharAnimWalkS);
|
I won't go into the details of the setCharacterAnimationForced function, but basically it takes a pointer to the character, and a pointer to the animation structure, then will take care of starting the animation. At that point, the character animation code automates keeping track of the animation, so the code for this particular character doesn't have to worry about it. (unless it wants to jump to a different animation sequence, or stop animation entirely).
Actually, in my implementation, there are a few more fields I use in these structures, to define WHERE the graphics data comes from, because my game will dynamically load tiles for the animations when necessary. But I didn't want to add to the confusion.
Ok, that was a long explanation. I hope that's useful.
Again, I'll repeat, there are probably a lot of flaws with my implementation. And it's a royal pain in the butt to define the animations this way. But it works for me. I just need to make a tool to simplify the creation of this data for me.
#70533 - gs2phateon - Tue Feb 07, 2006 3:09 am
Wow! Thanks for that explanation. That type of method was what I was hoping to use for animation. Thanks again!
#70615 - kashiwa - Tue Feb 07, 2006 8:13 pm
I have one suggestion for gauauu, if those codes were not only an example.
How about using a designated initializer with enum instead of #define definition?
Code: |
enum {
NORTH_FRAME_1,
NORTH_FRAME_2,
NORTH_FRAME_3,
...
};
const characterAnimDef mainCharAnim[] =
{
[NORTH_FRAME_1] = {
WALK_SPEED,
MCFrame(0),
MCAnim(1)
},
[NORTH_FRAME_2] = {
WALK_SPEED,
MCFrame(2),
MCAnim(2)
},
[NORTH_FRAME_3] = {
WALK_SPEED,
MCFrame(0),
MCAnim(3)
},
...
};
enum {
mainCharAnimWalkN,
mainCharAnimWalkS,
mainCharAnimWalkW,
mainCharAnimWalkE,
};
const characterAnimDef *mainCharAnimTable[] =
{
[mainCharAnimWalkN] = { (characterAnimDef*)&mainCharAnim[NORTH_FRAME_1] },
[mainCharAnimWalkS] = { (characterAnimDef*)&mainCharAnim[SOUTH_FRAME_1] },
[mainCharAnimWalkW] = { (characterAnimDef*)&mainCharAnim[WEST_FRAME_1] },
[mainCharAnimWalkE] = { (characterAnimDef*)&mainCharAnim[EAST_FRAME_1] },
};
setCharacterAnimationForced(mainChar, mainCharAnimTable[mainCharAnimWalkN]);
|
You can insert new frame without rewriting numbers.
#70665 - gauauu - Wed Feb 08, 2006 1:57 am
kashiwa, thanks!
I didn't know it could be done that way, but that really makes it easier. I was wondering if there was a nicer way to do it than what I was doing, but hadn't figured it out yet.
Good call, thanks for the suggestion.