📜 ⬆️ ⬇️

Translation SDL Game Framework Series. Part 5 - SDL Animation

Hello! It has been quite a long time since the translation of part 4 of a series of guides for the development of a gaming framework using the well-known SDL library. Now my schedule has been a little unloaded and I am ready to continue (not the last thing Andrei Firsov aka Vorobeez played in this, who literally pulled me out of the business stream with his own questions “And when is the continuation ?!”). In addition, I was recently transferred to read-only, and I want to restore the possibility of commenting on posts ...

In the last lesson we tried to make the popular game Tic-Tac-Toe. I hope most of you try to start the game were successful, maybe someone even managed to modify and improve the written code (and to some it all seems childish prattle). Even if it is not, try, try, and ultimately you will succeed.

In this lesson, we will try to upgrade our skills by adding animation to the piggy bank using 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 animation processing, 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 don’t want to use this approach. All criticism is better to send 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 now we will test only one object, which we will create a little later. And before we begin, let's add the include directive #include "CAnimation.h" 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 <SDL.h> 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 is worth telling a little about what this code does after all. In game dev, there is one main element of the animation that we need to handle - the current frame of the animation. Look at the image that we will use in this lesson. As you can see, we have 8 frames of dragon on one image. We will refer to the current frame by its sequence number 1,2, ... and draw it.



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

So, for the current frame number, 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 the storage of the sequence 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 the CurrentFrame is incremented by 1, we just shift by 64 pixels to the right in the image (frame size), and draw this frame.

As you may have guessed, MaxFrames tells us how many animation frames 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). The definition of the drawing frequency is programmed in the OnAnimate method:

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

By adding the old value of the elapsed time since the SDL operation and the desired frame rate, we can compare it with the time that the SDL (current) is already running. Chew: let's say we just started our program. SDL_GetTicks returns 0, and OldTime naturally also equals 0. We take it for granted that the desired frame rate is 1 frame per second. So FrameRate = 1000 (milliseconds). So, is 0 + 1000 greater than 0? Yes, i.e. we need to skip this beat and wait for the next one. But on the next clock cycle of 0 + 1000 less than SDL_GetTicks , this should mean that 1 second has passed. Thus, we increase the frame value, and then reset the OldTime value to the current time, and run the check from the beginning. I think now it’s clearer how it all works.

And now I will tell about Oscillate and FrameInc . It’s not that I want to confuse you by adding all this, rather the opposite, so I’ll 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 decrease. If we had 10 frames, for example, we would see 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 very simple! In the next lesson, I will show you an interesting way to use all of this. How does this work? Let's take a closer look at OnAnimate .

About OldTime and its purpose, we know, and what about the rest of the code? Look at checking the Oscillate value. Inside, we simply check to see if CurrentFrame has reached the maximum number of frames. If so, reset the CurrentFrame back to 0.

I think that by setting Oscillate to true , we will have a smoother display of animation! What do you think? Let's look at the purpose of FrameInc . The value of FrameInc is set to either 1 or -1, depending on whether we need to go on frames in one direction or another (decrease or increase). That is, if FrameInc is greater than 0, we start incrementing frames, otherwise we apply decrement. Thus, we alternately reach either 0 or the MaxFrames value, which, I think, 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 an object of the CAnimation class in CApp.h :

 CAnimation Anim_Yoshi; 

Set the value of MaxFrames, inside CApp_OnInit :

 Anim_Yoshi.MaxFrames = 8; 

If you need animation from 0 frames to 7 and back, set:

 Anim_Yoshi.Oscillate = true; 

Make the Yogi animate inside CApp_OnLoop :

 Anim_Yoshi.OnAnimate(); 

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

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

Well that's all! Compile our updated project and run it! Do not forget to download the picture from this article, because in the original frames in the column, that, IMHO, looks ugly. And a couple of moments. 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 key. Also, try turning off this annoying pink color from Yozhi (how to do this, too, can be seen in previous articles). Good luck to you, in the hard work of GameDeveloper'a! I hope these articles help ...

Links to source code:


Links to all lessons:

Source: https://habr.com/ru/post/169197/


All Articles