Saturday, October 6, 2012

Lesson 8: Timers

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:

That looks pretty good, so let's get started implementing these functions in timer.cpp, beginning with the constructor.

When we construct a new timer we want it to be off, ie. not started or paused. We can do this like so:

In our Start function we'll want to tag the timer as started and record the value of SDL_GetTicks when Start was called, so that we can properly return the elapsed time, which is the difference between the current SDL_GetTicks and the value of mStartTicks. In our Stop function we simply want to tag the timer as stopped, by setting mStarted and mPaused to false.

For our Pause and Unpause functions we'll need to do a bit more work. We wouldn't want to pause the timer if it wasn't started or was already paused as both operations don't really make sense. When we pause the timer we'll also need to store the elapsed time so that we can preserve the timer's value so that when we unpause we continue adding on to the measured time instead of resetting it. We can do this with the method discussed above, were we take SDL_GetTicks - mStartTicks as the elapsed time, where mStartTicks is the value of SDL_GetTicks when the timer was started. So Pause should look like this:

So now mPausedTicks stores the elapsed ticks, so we can resume timing when we call Unpause. In Unpause we'll want to mark the timer as not paused and use the value of mPausedTicks to set the value of mStartTicks to have some offset from the value of SDL_GetTicks at the time Unpause is called so that we preserve the value of the timer. We can do this by setting mStartTicks to SDL_GetTicks - mPausedTicks, so Unpause also ends up being quite simple:

Our function Restart, to restart the timer and return the elapsed time turns out to be quit simple. The function Ticks is used to get the elapsed time, then the timer is restarted and the value is returned. This ordering is very important as we need to get the elapsed time before we reset the timer, or else we'll return 0 every time.

Finally we arrive at the most important function of our Timer, the one that actually tells us how much time has elapsed! We'll only want to return a value if the timer has been started, if the timer is paused we'll want to return the value of mPausedTicks (the elapsed time between Start and Pause), if the timer is running we'll want to return return the elapsed time, with SDL_GetTicks - mStartTicks. If it's not started then we'll want to return 0, as no time has been measured.

We finish off our timer with some simple getters to check if the Timer is started or paused:

And there we have it! A useful, simple timer. Now let's try using it in a program to make sure it works. We'll make a very simple program that will display the elapsed time and read some input for starting/stopping/pausing/unpausing the timer. For this program we'll also need the Window class that we wrote in Lesson 7 to provide the various graphics functions we'll need.

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.

Here we also setup the message box positioning to stick a bit below the middle of the window height (recall that y is the y position of the top-left corner) and set its width and height equal to the texture's width and height.

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:

We then clear the stringstream by filling it with a blank string and set the positioning of the message to be a bit after the text message.

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.

Now before we render everything we've got one last problem to solve. How are we going to update the texture displaying the number of ticks on the screen? We can do something where we simply destroy the old texture and recreate a new one with the updated Ticks, but we wouldn't want to do this if the timer is paused or stopped since there'd be no reason to change the message.

Sounds like we'll just need an if statement, and to copy down the code we used to create the texture initially:

We can put this bit of code in our logic section right after the input polling. Our rendering section will just draw the two SDL_Textures with their Rect's and we finish off the program as always by destroying our textures, calling Window::Quit and returning.

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


End of Lesson 8

Thanks for joining me! I'll see you again soon in Lesson 9: Playing sounds with SDL_mixer.

13 comments:

  1. Wait... I clicked on "Lesson 9: Playing sounds with SDL_mixer" and nothing happened! /joking
    Looking forward for the next lessons, time to review everything until now and take a break.

    ReplyDelete
  2. 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).
    There'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!

    ReplyDelete
    Replies
    1. Fun fact: your blog is the first ever that I found teaching things in SDL 2.0!

      Delete
    2. Ah 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.

      Yea as far as other stuff on SDL2, it isn't officially out, but the RC is quite stable and worth it for the improvements.

      Delete
  3. 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).

    May 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.

    ReplyDelete
    Replies
    1. 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.

      For 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.

      Delete
  4. It would be cool if you did a tutorial on frames per second when animating sprites. Locking the frame rate for sprite animation.

    ReplyDelete
  5. Hi, I'm running tutorial on Linux and had some problems with makefiles. First of all, g++ cannot link libraries because of this line
    $(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

    ReplyDelete
    Replies
    1. 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.

      For 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!

      Delete
  6. 1.
    link 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

    }

    ReplyDelete
  7. Is there any way to subscribe this blog using RSS/Atom? ;)

    ReplyDelete
  8. I'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!

    ReplyDelete
  9. Thank you very much, this helped me learn SDL2 quickly. I look forward to future work from you.

    ReplyDelete