Translation of the SDL Game Framework Series. Part 5 - SDL Animation

Original author: Tim Jones
  • Transfer
  • Tutorial
Hello! It has been quite a while since the translation of part 4 of a series of guides for developing a game framework using the well-known SDL library . Now my schedule has unloaded a bit and I am ready to continue (not the last thing in this was played by Andrei Firsov aka Vorobeez , who literally pulled me out of the business stream with his questions “And when is the continuation ?!”). In addition, I was recently transferred to read-only, and I want to restore the ability to comment on posts ...

In the last lesson, we tried to make the popular Tic-Tac-Toe game. I hope that most of you have tried to launch the game successfully, maybe someone even managed to modify and improve the written code (and to someone this all seems like babble). Even if it is not, try, try, and in the end you will succeed.

In this tutorial, we will try to upgrade our skills by adding animation to the piggy bank using the SDL. As before, we will build on previous lessons (but not on TicTacToe , it was a separate cool project). I think it's time to start.

First of all, we need to create a new class for processing animations, and in the next lesson we will create a class for working with Entity(game entities). Please keep in mind that these two things are completely different in essence, and although I know that they can be easily implemented in the same class, I do not want to use this approach. All criticism is best sent to the author of the series, although any feedback will also be useful to me.

Create 2 new CAnimation.h and CAnimation.cpp files, respectively. Ultimately, when we create the CEntity class, it will inherit from CAnimation , but for now we will test only one object, which we will create a little later. And before we start, let's add the #include “CAnimation.h” inclusion directive to CApp.h (preferably before#include "CEvent.h" ).

Let's make some code!

Open CAnimation.h , and add the following lines:
CAnimation.h
#ifndef _CANIMATION_H_
    #define _CANIMATION_H_
#include 
class CAnimation {
    private:
        int    CurrentFrame;
        int     FrameInc;
    private:
        int     FrameRate; //Milliseconds
        long    OldTime;
    public:
        int    MaxFrames;
        bool    Oscillate;
    public:
        CAnimation();
        void OnAnimate();
    public:
        void SetFrameRate(int Rate);
        void SetCurrentFrame(int Frame);
        int GetCurrentFrame();
};
#endif


And in CAnimation.cpp we write this:
CAnimation.cpp
#include "CAnimation.h"
CAnimation::CAnimation() {
    CurrentFrame    = 0;
    MaxFrames       = 0;
    FrameInc        = 1;
    FrameRate       = 100; //Milliseconds
    OldTime         = 0;
    Oscillate       = false;
}
void CAnimation::OnAnimate() {
    if(OldTime + FrameRate > SDL_GetTicks()) {
        return;
    }
    OldTime = SDL_GetTicks();
    CurrentFrame += FrameInc;
    if(Oscillate) {
        if(FrameInc > 0) {
            if(CurrentFrame >= MaxFrames) {
                FrameInc = -FrameInc;
            }
        }else{
            if(CurrentFrame <= 0) {
                FrameInc = -FrameInc;
            }
        }
    }else{
        if(CurrentFrame >= MaxFrames) {
            CurrentFrame = 0;
        }
    }
}
void CAnimation::SetFrameRate(int Rate) {
    FrameRate = Rate;
}
void CAnimation::SetCurrentFrame(int Frame) {
    if(Frame < 0 || Frame >= MaxFrames) return;
    CurrentFrame = Frame;
}
int CAnimation::GetCurrentFrame() {
    return CurrentFrame;
}


I think it's worth a little talk about what this code does after all. In game dev, there is one main element of animation that we need to handle - the current frame of the animation. Look at the image we will use in this lesson. As you can see, we have 8 dragon frames in one image. We will refer to the current frame by its serial number 1.2, ... and draw it.



Remember, in the second lesson, we created a function to draw part of an image? It remains to apply it, coupled with our animation frame, and voila!

So, for the number of the current frame, we will have a variable with the talking name CurrentFrame. This is the current frame of the animation, which we will draw on the screen. All that he is responsible for is storing the serial number of the part of the surface that we will draw on the screen. So when we call our drawing function, it will look something like this:

CSurface::OnDraw(Surf_Display, Surf_Image, 0, 0, Ezhik.GetCurrentFrame() * 64, 0, 64, 64);

When CurrentFrame increases by 1, we just shift 64 pixels to the right in the image (frame size), and draw this frame.

As you may have guessed, MaxFrames tells us how many frames of animation we have. And finally, we need to know how many frames per second we draw, or rather how quickly this animation will be displayed (yes, yes, that same notorious FPS). Determining the rendering frequency is programmed in the OnAnimate method :

if(OldTime + FrameRate > SDL_GetTicks()) {
    return;
}

Adding the old value of the time elapsed since the SDL was running and the desired frame rate, we can compare it with the time that determines how long the SDL (current) is already running. I’m developing: let's say we just launched our program. SDL_GetTicks returns 0, and OldTime naturally also equals 0. Take for granted that the desired frame rate is 1 frame per second. T.O. FrameRate = 1000 (milliseconds). So, 0 + 1000 is greater than 0? Yes, i.e. we need to skip this beat and wait for the next one. But on the next measure 0 + 1000 is less than SDL_GetTicks , this should mean that 1 second has passed. Thus, we increase the value of the frame, and then reset the OldTime value to the current time, and start the check first. I think now it’s more clear how it all works.

And now I’ll talk about Oscillate and FrameInc . Not that I would like to confuse you by adding all this, rather the opposite, so I will show why this is necessary. In general, when Oscillate is set to true , the animation class will increase the frames until the maximum number of frames is reached, and then reduce it. If we had 10 frames, for example, then we would observe something like this: 0 1 2 3 4 5 6 7 8 9 8 7 6 5 4 3 2 1 2 ... (CurrentFrame). We go all the way from 0 to 9 and then go down in the reverse order. Everything is extremely simple! In the next lesson, I'll show you an interesting way to use it all. How does this work? Let's take a closer look at OnAnimate .

AboutWe are aware of OldTime and its purpose, but what about the rest of the code? Let's take a look at checking the Oscillate value . Inside, we just check to see if CurrentFrame has reached its maximum number of frames. If so, reset CurrentFrame back to 0.

I think that setting Oscillate to true will result in a smoother animation! What do you think? Let's look at the purpose of FrameInc . The value of FrameInc is set equal to either 1 or -1, depending on whether we need to go through frames in one direction or another (decrease or increase). That is, if FrameIncgreater than 0, we begin to increment frames; otherwise, we use a decrement. Thus, we alternately reach either 0 or the MaxFrames value , which, in my opinion , is much more beautiful (at least there is no hard reset of CurrentFrame to 0).

Finish line

Now let's put it all together! You need to create a CAnimation class object in CApp.h :

CAnimation      Anim_Yoshi;

Set the value of MaxFrames, inside CApp_OnInit :

Anim_Yoshi.MaxFrames = 8;

If you need animation from frame 0 to 7 and vice versa, set:

Anim_Yoshi.Oscillate = true;

Let's make Yoji animated inside CApp_OnLoop :

Anim_Yoshi.OnAnimate();

And of course, to enjoy the animation, you need to watch it by adding to CApp_OnRender :

CSurface::OnDraw(Surf_Display, Surf_Test, 290, 220, Anim_Yoshi.GetCurrentFrame() * 64, 0, 64, 64);

That's it! Compile our updated project and run! Do not forget to download the picture from this article, because in the original frames in a column that, IMHO, looks ugly. And a couple more points. Since the picture is .PNG, you need to use IMG_Load (in previous articles I wrote about this function) and connect the library to the project with the -lSDL_image switch . Also, try turning off this annoying pink color with Yogi (you can also see how to do this in previous articles). Good luck to you in the hard work of GameDeveloper! I hope these articles help ...

Source code links:


Links to all lessons:

Also popular now: