Spite: Into the Ratacombs is Diablo-like hack-and-slash game where our enigmatic protaganist, The Doctor, hacks and slashes and explosive potions their way through Zombies and Ratmen Shamans to curb the unnatural plague that scours the land.

It's the sixth game I worked on at The Game Assembly and the first game developed by Raveyard. Prior to this every game was made by a new randomized group of people but for the second year we would develop 3 games with the same group. These three games would be made with our own 3D game engine.

This was a hectic project as the game engine and game were being built in parallel, leading to the game looking very scuffed for a majority part of development as basic features were being implemented into the engine, but the end result is something I can look back at with pride.

The Component System

While an Entity-Component-System (ECS) would be the trendy way to structure our game engine, having worked with one before I argued against using one. As we had to build the engine and the game simultaneously there was too much work on our plate and not enough plate to also get all six programmers used to working in a completely foreign paradigm. Instead we decided on a simpler component-based system, more closely resembling the one we'd worked with before in Unity.

The resulting system is easy to use and by pooling the components frequent bottlenecks like dynamic memory allocation and fragmented memory are avoided, making the system "fast enough".

Threaded Pathfinder

Moving pathfinding, the textbook example for parallelizable tasks, onto a dedicated thread was in and of itself not a complicated task, concurrency is however a minefield and since it was a system that most of my peers were going to interact with, I wanted to make it sure that we got through the project with all limbs intact. This added complexity to the system but made accidental misuse difficult.

The pathfinder is queried for a path with a Request struct naturally containing the start- and end-points for the query but also callback functions for when the pathfinder has finished, if the pathfinding failed, etc. The pathfinder then returns a Ticket struct which is used for all future interactions with the system. The Ticket needs to be returned to the pathfinder to retrieve the path or cancel the request and since it is non-copyable doing so "uses up" the ticket.

Internally the pathfinder works like a thread-pool, each request queueing up as task to be completed by a worker thread, (As is there is only one worker thread but adding more is trivial in the unlikely case it should be needed.) when requesting a path a priority can be set, allowing agents that need to be responsive - like the player - to jump ahead in the queue and less important agents - like aesthetic critters scurrying about the dungeon floor - to be less of a drain on the system.

The Sprite Renderer

A simple but necessary and rather time-consuming affair. Size and position of sprites can be set either in 0-1 space ( (0, 0) being the top left and (1, 1) bottom right) or in "pixel space" ( (0, 0) once again as the top left of the screen)) and the pivot can be moved, making it easy for the user to align the sprite in whatever way is the most comfortable at the moment.
The system also includes what we called "world space sprites" which were used for health-indicators positioned above enemies, the interface is similar to the normal sprite system as a matter of convenience but with positions set - as implied - in world space. Interface aside, internally it naturally works like the engines normal 3D model renderer, the only differences being that world space sprites are always aligned towards the camera and their size on screen remaining the same regardless of distance.

The biggest challenges with this system was coercing information about the loaded textures from DirectX and forgetting that the GPU automatically applies perspective division, which needed to be undone for the world sprites.