This is part of an ongoing series where we write a complete 2D game engine in C++ and SFML. A new tutorial is released every Monday. You can find the complete list of tutorials here and download the source code from the projects GitHub page.
In this tutorial we will start to bring our little Viking to life by working on animation. We want to be able to, at the very least, have a walking and idle animation (although we will soon add attack, jumping, falling etc.). We also want to be able to easily and efficiently switch between the animations based on the players current state.
Before we can animate our Viking we will need the vikings animation sprites. You can download the individual images from here. Individual images are not ideal (more on this shortly) so we will want to combine our sprites into a single sprite sheet. I accomplished this using TexturePacker.
A sprite sheet is simply a collection of smaller images. The images are placed within the sprite sheet so that we can easily retrieve a single image (more on this when we build our animation class). The main benefit of having one texture for multiple images is that it helps to increase rendering performance. It allows our renderer to combine what would have been multiple draw calls into one. They can also help us with our animations, rather than each animation maintaining lists to many different textures on disk, we only need to store the position of the sprite within the sprite sheet for that frame. We’ll see an example of this shortly, but for now, let’s get started by creating an Animation class.
We start with an enum to store the data for each frame within an animation. This store’s everything we currently need to know about an animation frame and allows us to quickly iterate over different sprites within a sprite sheet to create our desired animations.
We need an animation class to act as a collection of frames. We’ll start with the minimum functionality that we require and build on it in future tutorials.
AddFrame allows us to add a frame to the animation. Its important to note that animation frames are played in the order they are added.
GetCurrentFrame simply returns the data for the current frame. We will use this soon to draw our animated sprites.
UpdateFrame checks if it is time to transition to the next frame. It returns true if the frame has changed. When a frames time is up it calls IncrementFrame, which increments the frame index by 1. If we’ve reached the end of the animation the frame index is looped to the beginning. In future, we will want to be able to define if an animation should play in a loop or play once and then stop or revert back to a previous animation.
Reset, as you have probably guessed, resets the animation to its initial state. This will be used to start playing from the first frame whenever we transition to an animation. For example, if we move from a walking to an idle state, we want to reset the idle state so it plays from the beginning frame rather than the frame it was on when we transitioned out of the animation.
Now we have the basic structure for an animation we need a way of adding an animation to an object. We’ll accomplish this by creating an animation component, called C_Animation.
We create an enum to store our objects Animation States. Currently we only want our player to walk and stand idle. We’ll add attack, jump, and a few other animations in a future tutorial and we may also want a way to load in states procedurally rather than hardcoded but for now this provides a simple method of switching between states.
1. Our animation component requires that an object has a sprite component. We’ll draw the animation frame using this component. We’ll need to add a few helper functions to the sprite component to enable us to do this, which we will do shortly.
We want to override the Awake method to retrieve a pointer to the objects sprite component. We also need to override the Update method to update our current animation. If the current animation is incremented we need to retrieve the new frame data and update our sprite with the new texture rect. The Update method will be responsible for setting our sprite based on our current animation frame.
1. We need to ensure that we have the right texture loaded. Ideally however each sprite in an animation will be part of the same sprite sheet.
Adding an animation is a straightforward process. We insert the pair (state and animation) into our collection. If we do not have a current animation set we will also set this.
There are three main steps to setting or changing an animation state:
- Check the state we are trying to change to is not already the current state. If it is then we return and do nothing more.
- Attempt to find the animation state in our collection. This makes sure we actually have the animation we want to transition to.
- If we do find the animation, we then set it as our current state and reset the animation (so it plays from the beginning). This animation will then be updated and displayed in the next call to Update.
You may have noticed that our animation component calls a SetTextureRect method in our sprite component that we have not yet created so let’s do that now. We’ll also make a small change to how we load our sprites. We want to limit the number of sprite texture changes by ensuring that do we do not set a texture unless it is different from our current texture.
Now we have our animation collection and component we can finally add it to our Viking object. We’ll do this in the OnCreate method in SceneGame.
1. We manually set our sprites width and height for now. In future, we will generate some form of data file (XML, JSON etc.) to do this for us.
2. We’ll start by creating an idle animation (we’ll create the walking animation next week).
3. Our idle animation has four frames so we need to call AddFrame four times. Each frame has the same width and height so I only need to work out there x and y position, which is the top left point for each sprite in the sprite sheet. Luckily for me, TexturePacker can generate a list of the sprites x and y positions. If you look in the resources folder for this tutorial you’ll find Viking.json. We don’t currently parse this file in our game (I have manually copied and pasted the sprite positions) but that’s definitely something we can add in future.
If you run the game our viking will now be animated. Goodbye static sprites!
In next weeks tutorial, we’ll add a walking animation and I’ll show you how we can switch between them depending on the characters current state. This has been quite a long tutorial so if you’ve stuck with it, well done! I hope it proves useful.
As always, if you have any suggestions for what you would like covered or are having any trouble implementing a feature, then let me know in the comments and I’ll get back to you as soon as I can. Thank you for reading 🙂
Have 60+ UFOs onscreen that have taught themselves to avoid each other and the sides of their environment.