top of page
FullBackground.png

2.1 Rundown

Team size: 3 (3 programmers)
Duration: 13 Days (3 weeks)

Rundown is a speedrun-inspired game where the objective is to get to the goal as fast as possible, and the input is recorded when the player holds any buttons, once they are released, it will playback, and a second is added to the timer. 

2.2 Pillars

We started looking at the game we wanted to make and took it from there. I personally have had a recent crush on the command pattern when combined with an event queue(see Gustav In Love Graph); we let this birth our game idea; aside from this, our goal was to choose patterns that would fit the game to not over-engineer the project trying to solve problems that didn't exist. Therefore, our first pillar was a given: KISS!
Due to the time constraint of the project and our inexperience as a group with SDL, we decided not to get over-excited and write lots of code that might not be used in the end.

GustavINLOveImage_edited.jpg

Command Pattern 
           +
  Event Queue

Gustav In Love Graph

Additionally, we realized that another way to save time is to avoid repeating ourselves. Therefore, our second and third pillars became DRY & YAGNI

2.3 The Plan

Since discussing patterns was on the board from the beginning, we started to look toward our pillars first to keep it simple. Therefore, we templated a full game loop and the engine layout in a markdown document.


This helped us grasp what classes were needed, where abstraction was necessary, and so on. In the beginning, we didn't know we would reach the number of required patterns by almost just setting up a base structure, so we planned for a Singleton Texture Manager, which didn't violate the Single Responsibility Principle.


Modern game engines and their workflow inspired us; therefore, we aimed to deploy the factory pattern in combination with the composite pattern to have an empty entity that could be defined when created.
 

The structure of the project (See UML).

RunDown (2).png

2.4 Execution

Since I've been the author or co-author of every script except the TextureManager.h/cpp, GameManager.h/cpp, and the StateMachine Implementation. I will, therefore, choose a small implementation authored by me.

First, I'd like to present the Input System within the game, which is a simple class for organizing input behind a facade pattern to hide complexity. My intention was to create an input class that is self-managing and easy-to-use, with only three accessible methods for reading input.

2.4.1 Input Event

The Update method reads the SDL events and writes to a map chosen for its low overhead, with an O(log n) lookup complexity. By clearing the map of pressed values for every frame and resetting the input for holding on KeyUp, the system allows for both pressed and held buttons.

Input

2.4.2 Input Call

To read input, any other programmer only needs to call either Input::GetKeyDown or Input::GetKey; this will read the correct value from the map with the same lookup complexity as writing. The GetKeyDown and GetKey will register any key requested if it's not yet on the map. Here, I weighed performance vs convenience; in a previous iteration, I registered all keys used in the engine initialization; this made it so that I didn't have to do a lookup on every frame on the map.

Input

2.4.3 Commands

Commands are neatly packaged using the command pattern as shown by the Gustav Packs Commands Graph, allowing any command to be used by the Event QueueWe chose this approach for multiple reasons: first and foremost, gameplay; the game was made to showcase a combination of an Event Queue and the Command Pattern. The combination provides a powerful, easy-to-use framework with loose coupling since the receiver is sent with the package; anyone can unpack it and invoke the action. 

GustavPacksBox.png

Gustav Packs Commands Graph

2.5 Post Mortem

The project as a whole was a success: a fantastic team of programmers who were engaged in both the course and subject, which led to amazing discussions and, as a result, a good end product.

None of us worked with smart pointers before, just normal raw pointers which we managed ourselves. This led to an overuse of shared_ptr, which came with issues with cyclic dependencies. In hindsight, using smart pointers was a good choice since we learned from our mistakes, and I now know how and when to use weak_ptr, unique_ptr, and shared_ptr.

Our UML graph clearly shows there was inheritance in some places where it was unnecessary that our Mono class was supposed to be the base class for much more than our entity's.

You do not often get to say that if you did it again, you would have made it similar. I would probably try to make a UML graph before we started rather than afterward; I would have done more research before starting to implement smart pointers.

UML
2.1 Rundown
2.2 Pillars
2.3 The Plan
2.4 Execution
2.5 Post Mortem
bottom of page