Mathias Dierickx

Engine programmer


2D Game Engine

This Game Engine was completely made by me, taking into account all of the well known programming patterns and designs.

About the project

The game engine was written taking into account software design patterns and game programming patterns. It includes a game loop, command pattern, update method, components, items pools, an event system and some Singletons that manage resources and input.

On top of these features, I decided to include a basic collision scene to the project as well. Currently, there is support for colliders(blocking) and overlappers(non-blocking) collisions. An objects can easily become a collider by adding the collision component to it. A collider can be set as static or, dynamic. Static colliders are only checked with dynamic colliders as it is assumed they will not move into each other during gameplay. Dynamic colliders will be checked with both all static and all other dynamic colliders. Ever collider component hold 2 vectors, one containing all overlapping objects and one containing all blocking objects. This way the programmer can react to overlaps and blocking collisions. The scene itself will not block any items from moving at this point, this is still the responsibility of the programmer creating the game in the engine.

For this whole project, I set up a Perfoce server to have source control. Ever since I started using it for this project, I have put all of my other projects on that server as well. This meant learning all of the administrator tools for Perforce, by now I'm fairly familiar with the whole program.

Code snippet

The following snippet contains the core code, that runs the game loop. The game loop, along with the Time, resourcemanager and input singletons are encapsulated in this main Engine class. This way I can access some specific friend functions in these singeletons that I don't want the game programmers to have access to.
The Initialize method sets the core settings for the engine. There is an option to lock the frame rate to a certain value, this is done by changing the msPerFrame value. However, this value is ignored when the lockFrameRate boolean is set to false. All other managers are also initialized in this method. The input manager looks for input from the keyboard or controllers and passes it to the correct object, using the command pattern. The renderManager has a pool of renderComponents, this way I cut down on memory allocations during gameplay. The initialize method is called with a pool size, this way you can change how much space is allocated for potential renderComponents. The TimeManager keeps track of the time and is responsible for locking the framerate if the framerate is locked. It is initialized with the msPerFrame value and the lockFrameRate boolean. If the framerate is locked the time.CalculateDeltaTime() function will make the main thread sleep for the needed time to lock the framerate to the desired value.

The next snippet shows the code for the RenderManager. This class has an object pool of RenderComponents, and can be called by a gameObject to get a new RenderComponent or to return a RenderComponent when an object gets destroyed. When the pool gets too small, it doubles the size of the pool. This means we will have some dynamic allocations during the game, but by doubling the size we make sure it doesn't happen too often. However, this can lead to memory waste if the pool gets doubled if only one object needed to be added. Therefore the programmer has to set an appropriate starting size of the pool, if memory is an issue. The Draw method will call the draw function on every active RenderComponent. The order in which these components are drawn is very important as we want some object to be displayed above others, this is handled by layers, where higher level layers are drawn on top of lower level layers. When the layer of one of the RenderComponents changes, it has to flag this to the Rendermanager so that all components can be sorted, so that the correct components will be drawn at the correct time. This is done by the SetNeedsSorting() function and is checked every frame before drawing.

The next snippet shows the code that makes the ghosts in the PacMan game run on different threads. First we have the header file, some of the functions are defined in line therefore I added the whole header to the code snippet. The three functions implemented here are the triggers for both the update and the lateUpdated methods and the exit method. These should be pretty straightforward. The trigger functions tell the thread they need to update. When the exit function gets called, the thread will detach and destroy itself when it's done.

The Run method will keep checking if the owned ghost has to be updated. This is only the case when the thread is flagged for an update (or lateUpdate) AND the atomic int GhostThreadUpdate (or GhostThreadLateUpdate) value is greater than 0. This ensures the ghosts only get updated once per frame, and is enforced by the update method of the PacManScene. If the thread's ghost pointer becomes invalid, the thread will break execution and return. After which it gets destroyed.