Animations
September 18th, 2024
Hello! Today, I'll be covering my animation system. The system supports both sprite sheets and individual frames for input. First I'll go into a brief explanation of my Sprite class, then the animations.
Sprite Class & Component
I'll start with my Sprite class. A sprite is initialized by passing it SpriteData:
The actual sprite just keeps references to things like the texture, surface, source rectangle, destination rectangle, etc:
The constructors call the appropriate version of CreateSprite, depending on the parameters. I had to implement the Rule of Three since the sprites are responsible for creating and deleting the texture and surface.
The Sprite Component is responsible for rendering the actual sprite. Here are the relevant members:
The SpriteComponent will only render the mCurrentSprite, which is set by the AnimationComponent. When no animation is playing, it will render the mDefaultSprite. The AnimationCompnent will call SwapSprite to change the mCurrentSprite:
And we call Update to match the mTransformComponent's dimensions!
I also created a supporting function called FlipSprite:
This function will be called for a run animation we'll make later, since we want the sprite to be facing the direction it's running in.
Animation Component
Similar to the SpriteComponent, the AnimationComponent will take in an array of AnimationData to create the animations:
The animationName will be used to call PlayAnimation later in the component. The other variables are settings that will be used by the component or the actual Animation class. The animsToBlock set defines animations that cannot interrupt this animation from playing.
The spriteData will be assigned by the user. If a sprite sheet is passed in, the AnimationData can be initialized by using this function:
This function supports a horizontal sprite sheet, like the one below. It simply scans the sprite sheet from left to right while adding to the spriteData array. The user will specify the dimensions of the srcRect, so we know what part of the sprite sheet counts as one frame.
Here is the actual Animation class. It has functions to help the Animation Component keep track of which frame we're on.
The sprites array stores the actual frames of animation. The timeStamp will be updated by the AnimationComponent. It denotes the last time the next animation frame was called. The two "Get" functions keep track of our currFrame and the two "Is" functions tell the AnimationComponent when to swap to the next frame:
Note that if the animation is looping, the IsAnimationDone function will never return true. This means another animation will have to be played over it or it will need to be stopped manually.
Now onto the component. Here are the members:
The animations will be stored in animations, using their animation-name as the key. When an animation is played, we'll look it up in the map and set the mCurrAnimation pointer to point to the value at that key. The default animation is used for any idle animations.
Here are the component's functions:
We'll step into the AddAnimations function first. This just creates animations from the provided list of AnimationData:
We pass in the value at mAnimations[anim.animationName] by reference so it can be created directly inside the map.
Here is the CreateAnimation function:
We call emplace_back so the Sprite constructor that takes SpriteData is called. It's a pretty simple process!
Here is the PlayAnimation function:
Here we make sure the playing animation isn't blocking the requested animation. In my game, the player will do fancy ball handling while running. I'll want the ball-handling animation to take priority over the running animation, so this animsToBlock array helps specify what animations can't override the current one. At the end, we set the mCurrAnimation pointer, which is handled by my Update function, that loops through the animation frames:
First, we try and stop the animation if it's finished. Otherwise, if we need the first frame or the next frame, we update the animation's timeStamp and swap the sprite in the SpriteComponent.
Character Class
My player derives from the Character class, which is simply a GameObject with a CollisionComponent and AnimationComponent added by default:
The Character's Init function will add any created animations to the AnimationComponent. This makes it so child classes only needs to override this function to add animation support!
And that's all! Let's see it in action…
Demonstration
I'll override the CreateAnimations function and make the AnimationData to give my Player animations. I'll give the player two animations, starting with a running animation. This animation will use a sprite sheet:
The most important difference is that the srcRect is specified. We set the width and height to be 27 x 35 pixels, which are the dimensions of a single frame. The x and y are set to 0, 0 because we'll start at the far left side of the image.
Then here is frame-to-frame animation for the taunt:
I'll make it so the taunt blocks the running animation for this showcase.
Now to play the animations, in my Update, we'll call HandleAnimations:
The mDeltaX/Y just represents the direction the player is moving. TryPlayAnimation is called so that the run animation doesn't interrupt itself. We also call FlipSprite to make the sprite face the right direction.
For the taunt, I'll have it play if the user presses the "F" key:
And here's the demonstration!
Conclusion
I'm pretty satisfied with my animation system. I was feeling a bit burned out by doing collisions, so this was a nice break to do something far less complicated. My next blog post will cover how I handle separate rendering layers!
Thanks for reading,
Jamari
Dribble King
Status | In development |
Author | MrMisinput |
Genre | Action |
Tags | Top-Down |
More posts
- Pitfalls and Lessons Part I16 days ago
- Dunking16 days ago
- Clock Class and Player Boost79 days ago
- Keyboard Controller79 days ago
- Render Layers87 days ago
- Collision Part III: Discrete Collision94 days ago
- Collision Part II: Sweep and Prune Detection95 days ago
- Collision Part I: Manager and Component96 days ago
- Entity Component SystemSep 03, 2024
Leave a comment
Log in with itch.io to leave a comment.