So you want to make a game engine? Part 8 – Animation

Hey everyone!  I really hope you guys are enjoying this series, and as always please feel free to leave feedback and ask questions!

For those following along with development, let’s take a quick look at what we have so far:

  • We’ve discussed the purposes of a game engine and described the things it must be able to do.
  • We’ve implemented a double-buffered drawing system and implemented a Vector class.
  • We’ve written the skeleton framework of a game project created with our engine and looked at how it all works together.
  • We have a way to encapsulate the data of an in-game object and have a method of drawing it.
  • We can check for collisions between objects and simulate bouncing objects.
  • We can detect and handle input from the mouse and keyboard.
  • We can draw objects with rotation applied, as well as check for collisions accurately between objects with rotation applied

As mentioned before, the core of our game engine is done, and at this point we’re adding some neat graphics tricks before we start building our game (which I’ll introduce next time!).  Next time we’ll finally implement the ability to use shapes instead of images as our Sprites, but today let’s talk about animation!  We’ll also briefly discuss some other programming topics like threads and memory management.

How do we approach this?

Specifically, the type of animation that we’ll be implementing is frame-based animation.  Think about an animated GIF image, which is a sequence of ‘frames’ that are played back at some speed, which is called the frames-per-second (or FPS).  Similarly, our animated sprite will be made up of an image array that contains each frame of the animation, and a frames-per-second rate at which to animate them.  Animation goes from the first frame to the last frame, and loops if the animation is set to loop infinitely.

Back up for a second: we already have an image array in our Sprite class, remember?  This is particularly why we implemented it that way: we can now add the animation stuff without having to rewrite the parts of the code that use a single static image as the sprite.  Yay for planning ahead!

One way of transitioning between the frames of the animation would be simply to move to the next frame at each tick of the game loop.  The issue with this though is that we’d either be forced to animate the sprite at the same speed as the game loop, or write additional code to convert it to ‘sprite ticks’ to get the sprite to animate at the desired FPS.  This is cumbersome and can be bug-prone, so we’ll implement the animation using threads instead.  The benefit of using threads is that the sprite’s animation thread can run separately from the game engine’s thread that manages the game loop, and thus they will be able to run at different speeds and everything will still work together nicely.  We still have to be careful though, as threads can sometimes be weird to work with and may cause unintended behavior if not implemented correctly.

All of the work today (well, the majority of it anyway) is happening in the Sprite class.  Let’s get started!

Animation properties

Before we start writing the code that handles the animation, we need to add a couple properties to our Sprite class.  We already have a current frame property (CurrentFrame) and the image array itself (Images).  We need to add three more things in order to implement frame-based animation: the FPS at which to animate the sprite, a Boolean that determines if the animation should loop once the last frame is reached, and a Boolean we can check to see if the Sprite is currently animating itself.  We can add all three of these at the top of the file near our other animation-related properties:

    Private _fps As Integer = 30
    
    Public Property FPS As Integer
        Get
            Return _fps
        End Get
        Set(value As Integer)
            _fps = Math.Max(value, 1)
        End Set
    End Property

An FPS of zero doesn’t make sense, so we’ll use the Max() method to set it to 1 if something tries to set the FPS of zero or less.

    ...
    
    Public Property LoopAnimation As Boolean = True
    Public Property IsAnimating As Boolean = False

That’s it for the properties!  Normally, if we don’t specify a default value, Visual Basic will assume the initial values of LoopAnimation and IsAnimating are False; for the IsAnimating property this is fine, but we’ll set the default value of animation looping to True, since most animated objects in games loop their animations indefinitely.

Frame control

Our animation is going to be controlled by two simple methods: NextFrame() and PrevFrame().  The names of these method are pretty self-explanatory of their purpose; they either increment or decrement the current frame index.  The catch is that when either of them reaches the bounds of the image array (if we try to advance past the end of the array, or go to a negative index), we need to make sure we loop it around so that we don’t run into IndexOutOfRangeExceptions.

    Public Sub NextFrame()
        If _curFrame = -1 Then Exit Sub
    
        If _curFrame = _imgs.Count – 1 Then
            _curFrame = 0
        Else
            _curFrame += 1
        End If
    End Sub
    
    Public Sub PrevFrame()
        If _curFrame = -1 Then Exit Sub
    
        If _curFrame = 0 Then
            _curFrame = _imgs.Count -1
        Else
            _curFrame -= 1
        End If
    End Sub

We now have two methods we can call manually to change the current frame of the animation, which can be useful despite not being automated!  We could, for example, have an object representing a castle in a game that has a series of images portraying in varying states of disarray: each time it is struck, NextFrame() can be called on its Sprite so that it appears to be taking damage when hit.

One more note: notice the conditional check for the current frame being set to -1.  How would we ever get this value?  This is another example of planning ahead: in the next post, we’ll be adding support for using shapes instead of images for the Sprite, including rectangles and circles.  Obviously, a shape can’t have a frame-based animation associated with it, so when we initialize a Sprite as a shape, we’ll set the current frame index to -1, which indicates to the animation functions that this Sprite is not capable of animation.  Thus, if the animation methods are called anyway, they will have no effect.

This is just one of the possible uses for manual frame control like this, and NextFrame() will be used in our animation thread to advance the animation.  So before we code that in, let’s discuss threads for a moment.

Animation and threads

Many programs you’ve written can be looked at as a single sequence of instructions that are executed in the order that they are written.  Some instructions may branch to other methods or functions, but generally your program is doing one thing at a time.  This is called a single-threaded program.  A multi-threaded application, on the other hand, can create threads, which are also sequences of instructions that run in parallel to the main application thread, which allows the application to do multiple things at once.

Since games typically need to do a lot of things at once, the potential uses for threading are numerous in game development.  Here, we’ll use threading for animation: we’ll create an animation thread that handles only the sprite’s animation and nothing else.  This will allow the animation to run independently of the game loop, and we can start and stop the animation whenever we want without affecting anything else in the game.

We can view a thread as a “subprogram”: it is created and started, and then runs until it reaches the end of the code it’s supposed to execute.  The thread is said to “die” when it reaches the end of its code, but we want the animation to continue until we tell it to stop.  Thus, we simply enclose everything in a while loop where the exit condition is when the sprite’s IsAnimating property becomes False.

Let’s start implementing this.  Under PrevFrame(), we’ll declare the thread object that we’ll use to create the animation thread.

    Private tAnimate As Threading.Thread

Next, we’ll create the subprogram that will be executed by the thread, which will control the animation of the sprite.  The idea is really simple actually:

  1. Advance to the next frame by calling NextFrame()
  2. If we’re at the end of the animation, check if we’re supposed to loop it. If not, set IsAnimating to false.
  3. Allow the application to handle Windows messages.
  4. Sleep for an interval defined by the animation’s FPS before advancing to the next iteration of the loop. If the thread receives an Abort request while sleeping, stop the thread.

    Private Sub Animate_Thread()
        While IsAnimating
            NextFrame()
    
            If Not LoopAnimation And (_curFrame = _imgs.Count – 1) Then
                IsAnimating = False
            End If
    
            Application.DoEvents()
    
            Try
                If IsAnimating Then Threading.Thread.Sleep(1000 / _fps)
            Catch ex As Threading.ThreadAbortException
                IsAnimating = False
                Exit While
            End Try
        End While
    End Sub

All that’s left to do is write the methods that start the animation thread and stop it.  They’re pretty simple.

    Public Sub StartAnimating()
        If _curFrame = -1 Then Exit Sub
    
        IsAnimating = True
        tAnimate = New Threading.Thread(AddressOf Animate_Thread)
        tAnimate.Start()
    End Sub

When we initialize the new thread object, we need to give it the address of the method we want it to execute.  We give it the address of the Animate_Thread method, so when we start the thread it begins executing this method.  Since everything in Animate_Thread() is in a while loop, it continues to run until we change IsAnimating to False, which is the only job of the StopAnimating() method:

    Public Sub StopAnimating()
        If _curFrame = -1 Then Exit Sub
    
        IsAnimating = False
    End Sub

However, one of the pitfalls of threads arises here: suppose a game programmer forgets to call StopAnimating() on a Sprite when the game is shutting down.  Remember that the animation thread is running separately from the main application thread; thus, even if the main application thread is stopped and exits normally, the animation thread will continue to run if IsAnimating remains True, which will prevent the application from exiting fully.  The easiest way to correct this and account for a forgotten StopAnimating() call is to make the Sprite and GameObject classes eligible for garbage collection, and stop the animation thread when the object is to be disposed of.

Garbage collection

When you initialize objects in a language that supports garbage collection, such as Visual Basic, other .NET languages, and Java, the language runtime allocates the required amount of memory to store the object in memory (in a part of the application’s memory called the heap).  This can only succeed if there is enough memory in the heap available for the object; if the process determines that there isn’t enough memory to allocate for a new object, the garbage collector comes into play and checks for objects in memory that are no longer being used and performs any operations needed to reclaim the memory they were using.  These cleanup operations are typically placed inside a Dispose() method, which is called by the garbage collector when it decides to claim the memory that the object was using (in other languages such as C++, these operations are placed inside an objects destructor method).  The Dispose() method is also called when the application is exiting and it needs to release the memory resources used by its objects.

You can probably see where this is going: to fix this problem of threads not stopping with the main application thread, we need to include a Dispose() method in our Sprite class that calls StopAnimating() to stop the thread, and we need a Dispose() method in our GameObject class that calls the Sprite’s Dispose() method.  Thus, when the application is told to shut down, all running animation threads are stopped and we don’t have to worry about the program not exiting correctly.

While that may seem like a long and complex description, it’s relatively easy to implement.  All we need to do is implement the IDisposable interface in our GameObject and Sprite classes, which allows us to define a Dispose() method that is called automatically by the system’s garbage collector when it decides to free up memory.

Visual Studio does most of the work for us when we want to implement an interface by automatically creating the methods defined by the interface.  To implement the IDisposable interface, at the top of the Sprite.vb file right underneath the class definition, add the implements statement as shown:

    ...
    
    Public Class Sprite
        Implements IDisposable
    
    ...

Notice that as soon as you press Enter after the Implements statement, Visual Studio adds a bunch of stuff to the end of the file.  This is the code required to be implemented for the IDisposable interface that is run when the Dispose() method is called on the class, either by the garbage collector or by calling Dispose() directly.  The only section we need to modify is within the Protected Overridable Sub Dispose(disposing As Boolean) method.  Here, we need to make sure the animation thread is stopped and also dispose of the image resources used by the Sprite.

    ...
    
    If Not Me.disposedValue Then
        If disposing Then
            IsAnimating = False
            For Each i As Image In _imgs
                i.Dispose()
            Next
            _imgs = Nothing
        End If
    
    ...

Now when the garbage collector starts cleaning up unused memory, it stops the animation thread and disposes of the image resources correctly.  Perfect!  Just one more thing: we need to do the same thing to the GameObject class, so that when it is disposed of by the garbage collector, it can also issue a call to its Sprite’s Dispose() method.

We’ll add the Implements statement the same way as before:

    ...
    
    Public Class GameObject
        Implements IDisposable
    
        ...

In the Dispose() method that is generated, we’ll just add a simple call to the Sprite’s Dispose() method, so that when the garbage collector decides it’s time to clean up after the GameObject, it cleans up its Sprite as well.

    ...
    
    If Not Me.disposedValue Then
        If disposing Then
            Me.Sprite.Dispose()
        End If
    
    ...

Wrap Up

That does it for today’s post!  There is actually an example project you can look at which uses animation and answers the challenge prompt from last time!

Next week we’ll add shapes and additional Buffer drawing functions, and briefly talk about some object-oriented programming concepts that can be useful in game development.  Finally, we’ll put it all together and run through the development of (okay fine I’ll spoil it) a side-scrolling 2D space shooter 😀

You can download the code from today’s work here: Dropbox

Question: We’ve discussed that threads can be used to do something without preventing the main application’s thread from doing whatever it’s doing, meaning that we can run different sections of code concurrently.  Besides managing animation, what other potential uses do multiple threads have in game development?

Challenge: No challenge today!

This entry was posted in Game Designers and Developers. Bookmark the permalink.

Leave a Reply