Thoughs on sprite animation organization

Started by
3 comments, last by jdindia 15 years, 1 month ago
Hey, everyone. I've been reading topics here a lot over the years, but don't post much. Recently, I've started programming my own sprite-based platformer using the old NES Mega Man games as a model to work and learn from. Going through some of the sprite sheets I found online, I noticed that each animation has a different number of frames. The run cycle, for instance, is only about 4 frames and the jump is 1. After a lot of deliberation and some experimentation programming, I decided on a system that lends itself to a lot of flexibility but isn't as organized as I would like. basically it looks like this. A frame object contains coordinate information of the location and size of the frame on the master sprite sheet. An animation object contains a vector of these frame objects and each sprite contains a vector of animation objects. So if I wanted to access a frame in a run cycle, I could make the call as follows: spriteObject.vectorOfAnimations.at(animationNumber).vectorOfFrames.at(frameNumber); where animationNumber and frameNumber are integers like 2. I'm curious on your opinions and thoughts about this system. I'm considering using arrays to be more efficient with access time, but my main concern is loosing track off all this coordinate information and the confusingly lengthy calls. I could write a function making it easier to interpret by assigning specific indexes in the animation vector an enumeration like "RUN" so I could call spriteObject.animate(RUN), but the underlying structures remain the same and this is further building on top of an already confusing system. Does anyone have a simpler approach that won't limit the number of frames and animations a sprite can have?
Advertisement
I usually push this out to a text file. Something like (I'm being extra-verbose):
run_animation file: mega-man.png x: 0 y: 0 w: 128 h: 128 cell_w: 32 cell_h: 32
jump_animation file: mega-man.png x: 0 y: 128 w: 128 h: 32 frames: 4

Then you'd have something like an animation definition class which would parse that up, find a reference to the image in memory, and store it. Your actual animation can then get the image it needs and the relevant frame data.

I wouldn't bother with some complicated abstract array system like you've built. Keeping your objects simple is good and besides, you may want your sprite sheets to have a bit of non-uniformity.
Your idea to import coordinate information is good actually; I'll probably want to do that before I finish the project, but even if I predefine it in a file, I'd still have to structure the frame objects into an organized animation system UNLESS I perform I/O every time I want to access a frame.

Opening and parsing an entire file to find my a specific frame FOR EACH sprite is not something I want to do. After I read the information into my code, I still need to structure it somehow and that's the trouble I'm having.

The simplest solution I've been able to think of is to create several subclasses from sprite like FlyingEnemy or Hero or Boss which have specific animation sequences and lengths. For instance, Hero would have array members like

//Hero animation arrays
Frame runCycle[4]
Frame jump[1]
Frame shoot[2]

and other classes would be different like this

//FlyingEnemy object animation
Frame swoopAndShoot[3]
Frame shoot[2]
Frame explode[4]

That'll allow me to remove one nested layer of animations and keep track of which animations are in the object. I could simply call hero.runCycle[2] to get the 3rd frame of the run cycle animation. If there were a way to dynamically declare the size, names and numbers of arrays I'll need within these class headers, that'd be great, but I think this'll have to do for now. I'm not about to throw away c++ for one limitation.

Thanks for the IO suggestion though! That'll definitely keep things organized.
There are many different ways of doing this kind of thing, but let me give you one possible view of it.

First you have your image container. It stores the image data and has a map from names to images for quickly figuring out if something is in the container.
class ImageContainer{getImage ( name )addImage ( image, name )vector<Image *> _imagesmap<string, int> _names}


Then you have something like your animation. I'm going to compact this to contain more than some people would say it should (I'm breaking srp).
class Animation{load ( string ) // from the text fileupdate ( float deltaTime )getImagegetTextureCoordinates // the sub-rectangle that contains the current cellclonestring _name;Image *_imagefloat _time;float _secondsPerFrame;int _currentCellint _frames// ... whatever else you need for calculating your animation}


Then you have some kind of factory or container for animations.
class AnimationFactory{loadAnimations ( filename )cloneAnimation ( name ) { return _animations[_map[name]]->clone() }vector<Animation *> _animations;map<string, int> _names;}


Finally you have your game object (actor, entity, whatever you like to call it)
class ObjectMegaMan{initialize{    _runningAnimation = animationFactory.cloneAnimation ( "mega_man_runs" );    _jumpingAnimation = animationFactory.cloneAnimation ( "mega_man_jumps" );    _standingAnimation = animationFactory.cloneAnimation ( "mega_man_stands" );    _currentAnimation = _runningAnimation;    _state = running}update ( float deltaTime ){    switch ( _state )    // do some logic, change animations as needed    _currentAnimation->update ( deltaTime );}render{    bindImage ( _currentAnimation->getImage )    setTextureCoordinates ( _currentAnimation->getTextureCoordinates );    // etc.}_stateAnimation * _currentAnimationAnimation * _runningAnimationAnimation * _jumpingAnimationAnimation * _standingAnimation}


Something like that anyway.
I guess you have sprites, so instead of render you'd have

void update ( float deltaTime ){    // logic stuff as before    _currentAnimation->update ( deltaTime );    _sprite->setAnimation ( _currentAnimation )}


Or you could put the update function on the sprite and call set animation when you switch animations (ie, there is no _currentAnimation, only _sprite). Or you could do something like
sprite->setSubRect ( _currentAnimation->getTextureCoordinates )


As with everything, there are many ways of doing things. It just depends.

This topic is closed to new replies.

Advertisement