Deprecation Notice:
I've been updating the lessons now that SDL2 is officially released, please visit my new site on Github that has updated lessons and improved code.In this lesson we'll add onto our small class library of one (the Window class) by creating a simple timer and then use it in a simple program. To do this we'll be making use of the SDL_Timer functions, specifically SDL_GetTicks which returns the milliseconds elapsed since SDL was initialized.
So how would we be able to measure a time if we can only tell how long it's been since SDL was initialized? Well we could mark down the value of SDL_GetTicks when we start the timer as startTicks and then again when we stop it as endTicks. We can then subtract endTicks - startTicks to get the milliseconds elapsed during the measurement. Seems easy enough right?
Let's begin planning out how we'd like our Timer to function before we start putting it together. We'll definitely need functions for starting, stopping and getting the elapsed ticks (measured in milliseconds), and we'll also want to be able to check if the timer has been started. In addition I think it'd be nice if we could pause and unpause the timer, and maybe a function to restart it that would return the elapsed ticks and start the timer over again all in one go. We'll also need variables to track the start and pause points as mentioned above in how to use SDL_GetTicks to determine the elapsed time, along with some values to track the state of the timer.
So let's try something like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef TIMER_H | |
#define TIMER_H | |
/** | |
* What we think our timer should look like | |
*/ | |
class Timer { | |
public: | |
Timer(); | |
///Start the timer | |
void Start(); | |
///Stop the timer | |
void Stop(); | |
///Pause the timer | |
void Pause(); | |
///Unpause the timer | |
void Unpause(); | |
/** | |
* Restart the timer and return the elapsed ticks | |
* @return The elapsed ticks | |
*/ | |
int Restart(); | |
/** | |
* Get the elapsed ticks | |
* @return The elapsed ticks | |
*/ | |
int Ticks() const; | |
///Check if Timer is started | |
bool Started() const; | |
///Check if Timer is paused | |
bool Paused() const; | |
private: | |
int mStartTicks, mPausedTicks; | |
bool mStarted, mPaused; | |
}; | |
#endif |
When we construct a new timer we want it to be off, ie. not started or paused. We can do this like so:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Timer::Timer() | |
: mStartTicks(0), mPausedTicks(0), mStarted(false), mPaused(false) | |
{ | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void Timer::Start(){ | |
mStarted = true; | |
mPaused = false; | |
mStartTicks = SDL_GetTicks(); | |
} | |
void Timer::Stop(){ | |
mStarted = false; | |
mPaused = false; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void Timer::Pause(){ | |
if (mStarted && !mPaused){ | |
mPaused = true; | |
mPausedTicks = SDL_GetTicks() - mStartTicks; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void Timer::Unpause(){ | |
if (mPaused){ | |
mPaused = false; | |
mStartTicks = SDL_GetTicks() - mPausedTicks; | |
mPausedTicks = 0; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
int Timer::Restart(){ | |
int elapsedTicks = Ticks(); | |
Start(); | |
return elapsedTicks; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
int Timer::Ticks() const { | |
if (mStarted){ | |
if (mPaused) | |
return mPausedTicks; | |
else | |
return SDL_GetTicks() - mStartTicks; | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
bool Timer::Started() const { | |
return mStarted; | |
} | |
bool Timer::Paused() const { | |
return mPaused; | |
} |
I'll only be posting code relevant to the specifics of using the timer in the lesson, but if you have difficulty with some code that isn't posted you can always find the full source and assets for each lesson on Github.
After opening our Window we'll want to create an instance of the Timer class and some SDL_Textures to hold our messages.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Our timer: | |
Timer timer; | |
//Textures to display a message and ticks elapsed | |
SDL_Texture *msg = nullptr, *ticks = nullptr; | |
//Color for the text | |
SDL_Color white = { 255, 255, 255 }; | |
//Rects for the text | |
SDL_Rect msgBox, ticksBox; | |
//Setup msg text | |
msg = Window::RenderText("Ticks Elapsed: ", "../res/Lesson8/SourceSansPro-Regular.ttf", | |
white, 30); | |
//Setup msg dstRect | |
msgBox.x = 0; | |
msgBox.y = Window::Box().h / 2; | |
//Query w & h from texture | |
SDL_QueryTexture(msg, NULL, NULL, &msgBox.w, &msgBox.h); |
We also want to display the value of the timer's Ticks function, the elapsed time, after the end of the "Ticks Elapsed: " message. There's one small issue though, Ticks returns an int, but we need a string to render a message with. This can be resolved by using a stringstream; we write Ticks to the stream and then pass it to the message creation function as a string, like so:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Setup ticks message | |
//We must use a stringstream to convert int to string | |
std::stringstream ssTicks; | |
ssTicks << timer.Ticks(); | |
ticks = Window::RenderText(ssTicks.str(), "../res/Lesson8/SourceSansPro-Regular.ttf", | |
white, 30); | |
//clear the stream | |
ssTicks.str(""); | |
//Setup the ticks dstRect | |
ticksBox.x = msgBox.w + 20; | |
ticksBox.y = Window::Box().h / 2; | |
SDL_QueryTexture(ticks, NULL, NULL, &ticksBox.w, &ticksBox.h); |
Within our event polling loop we'll want to check for some key presses to tell the timer to start/stop/pause/unpause as desired.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//If user presses any key | |
if (e.type == SDL_KEYDOWN){ | |
switch (e.key.keysym.sym){ | |
//Start/stop the timer | |
case SDLK_s: | |
//If timer was running, stop it | |
if (timer.Started()) | |
timer.Stop(); | |
else | |
timer.Start(); | |
break; | |
case SDLK_p: | |
if (timer.Paused()) | |
timer.Unpause(); | |
else | |
timer.Pause(); | |
break; | |
//For quitting, escape key | |
case SDLK_ESCAPE: | |
quit = true; | |
break; | |
default: | |
break; | |
} | |
} |
Sounds like we'll just need an if statement, and to copy down the code we used to create the texture initially:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//If the timer is running, update the ticks message | |
if (timer.Started() && !timer.Paused()){ | |
//Update text | |
ssTicks << timer.Ticks(); | |
SDL_DestroyTexture(ticks); | |
ticks = Window::RenderText(ssTicks.str(), "../res/Lesson8/SourceSansPro-Regular.ttf", | |
white, 30); | |
ssTicks.str(""); | |
//Update w/h | |
SDL_QueryTexture(ticks, NULL, NULL, &ticksBox.w, &ticksBox.h); | |
} |
When you run the program you should see something like this:
Where the timer will start at 0 and begin increasing once you start it. Pausing will cause the timer to stop counting, unpausing will resume it from where it left off. Stopping the timer will stop it, and when restarting it will begin again at 0. Note that the value displayed doesn't reset when the timer is stopped but rather when it's resumed.
Lesson 8 Extra Challenge!
Make an additional message display to state whether the timer is stopped/paused
Hint
Wait... I clicked on "Lesson 9: Playing sounds with SDL_mixer" and nothing happened! /joking
ReplyDeleteLooking forward for the next lessons, time to review everything until now and take a break.
Hey there TwinkleBear, I just thought of dropping by to suggest something (and I'm sure you can handle this after passing by your github page heh).
ReplyDeleteThere's this blog: http://www.parallelrealities.co.uk/2011/10/intermediate-game-tutorial-1-displaying.html
It has a series teaching individual parts of SDL just like you're doing now, but further he teaches some intermediate-level stuff which is awesome because that's when people mostly get lost, and it's in a different "level" (intermediate) so you can build on top of things previously explained, which is even nicer to a progressive teaching & learning curve.
Cya!
Fun fact: your blog is the first ever that I found teaching things in SDL 2.0!
DeleteAh interesting. This is a bit my goal too, to make something like the lazy foo tutorials http://lazyfoo.net/SDL_tutorials/index.php but for SDL2, although I've gotten a bit tied of with classes lately heh.
DeleteYea as far as other stuff on SDL2, it isn't officially out, but the RC is quite stable and worth it for the improvements.
That's nice to know, I've shared this blog in facebook and twitter already but it seems I'm a bit over excited for the next tutorials lol... please, just take your time; unlike some people I do like when you introduce optimized things in these tutorials such as unique_ptr, and make use of classes (I get to understand game archicteture much better if it's OO).
ReplyDeleteMay I ask you something? I'd like to know some book or article that tells about memory leakage and good practices in this regard, I've done a very bad game (procedural yikes!) and it works, but the memory usage just rockets up (I guess my mouse detection algorithm to detect cards in the memory game's board is awful!).
Anyways, keep up the good work, I'm even thinking of translating these tutorials into my native language as well.
Oh neat! Yea translations are cool, someone else translated it into Chinese, maybe I should put some links on the side or something to the translations.
DeleteFor memory stuff I think any good C++ book would cover it pretty in depth:
Accelerated C++ is shorter and aimed at teaching people familiar with other languages but new to C++: http://www.amazon.com/dp/020170353X/
Or if you're more of a beginner or want a book that covers C++11 as well then C++ Primer 5th Ed. is good, and aimed at people completely new to programming: http://www.amazon.com/dp/0321714113/
I'm not sure if there are any books specifically but I think either of these books will go into tons of detail about memory management. I've got a copy of Primer which has been really handy, especially since it covers C++11 and more advanced features of C++ too.
It would be cool if you did a tutorial on frames per second when animating sprites. Locking the frame rate for sprite animation.
ReplyDeleteHi, I'm running tutorial on Linux and had some problems with makefiles. First of all, g++ cannot link libraries because of this line
ReplyDelete$(CXX) $(LDFLAGS) $(OBJS) -o $@
It works if you change OBJS and LDFLAGS like this
$(CXX) $(OBJS) $(LDFLAGS) -o $@
And I had an error with SDL_ttf runtime library, I found the way to resolve it - changed line
SDLLIB = -L/usr/local/lib -lSDL2 -lSDL2_image -lSDL2_ttf
to
SDLLIB = -L/usr/local/lib -lSDL2 -lSDL2_image -lSDL2_ttf -Wl,-rpath=/usr/local/lib
Interesting, it did work on my ubuntu vm with $(CXX) $(LDFLAGS) $(OBJS) -o $@, I'll see about switching it if that's an issue on other machines.
DeleteFor optionally setting the runtimepath: -Wl,-rpath=/usr/local/lib I didn't know about these flags! Very useful to know, I'll add them to the makefiles to set the runtime library search path appropriately. Thanks!
1.
ReplyDeletelink SDL2_mixer.lib
#include "SDL_mixer.h"
2.
Paste "SDL2_mixer.dll" and "smpeg2.dll" in project folder
3.
void init_audio()
{
SDL_Init(SDL_INIT_AUDIO);
Mix_OpenAudio(22050,AUDIO_S16SYS,2,640);
Mix_Music *mp3;// for mp3
//Mix_Chunk *wav; for wav
mp3 = Mix_LoadMUS("musicfile.mp3");// for mp3
//wav = Mix_LoadWAV("soundfile.wav"); for wav
Mix_PlayMusic(mp3,1); // for mp3
//Mix_PlayChannel(-1,wav,0); for wav
}
Is there any way to subscribe this blog using RSS/Atom? ;)
ReplyDeleteI'm not normally the type to comment, just wanted to say how much I appreciate your tutorials. In two days you've helped me understand the fundamentals of this library very clearly and taught me a few things I didn't know anything about previously. Thanks very much!
ReplyDeleteThank you very much, this helped me learn SDL2 quickly. I look forward to future work from you.
ReplyDelete