Gloomstone Devlog #1
What the heck is "Gloomstone"?
"Gloomstone" is the working title for my in-progress first-person dungeon crawling game engine. Development is rather slow, being a side-project, but I've been making more visible progress lately. I'm trying to spend a few hours each week pushing it forward.
The engine is written in TypeScript and uses WebGL to render. My goal is to have as few dependencies as possible. As such, there are zero run-time dependencies, and only a few build-time dependencies: TypeScript, esbuild, and Prettier.
Architecturally, the engine is a "pure" Entity component system. "Pure", to me, means that components have no behavior and systems have no state. Since classes are the very definition of coupling state and behavior, the
class keyword does not appear anywhere in the source code. Of course this decision has trade-offs, and it's been an interesting exercise.
From the beginning of this project, I decided that Tiled, a free and open source map editor, would be the source of truth for as much game data as possible. This helps strengthen the divide between source code and data, which I find to be in service of my architectural goals.
Whenever possible, I try to ensure that Tiled's basic features, such as ad-hoc objects with a sprite, work out of the box and as expected in the engine.
Tiled's Custom Types are used to define components, and Templates for prefabricated entities.
After implementing basic collision with the static walls, I wanted to add some button and door mechanics to make the world more interactive.
Buttons and doors are decoupled: Buttons respond to an interaction by sending a signal to a target entity (which is defined in Tiled). A door is a receiver of this signal and takes care of toggling and animating its state.
(The button is not currently visualized in the gif above, but exists on the wall tile with the sewer grate.)
This was a simple feature, allowing entities be laid flat as well as upright. Useful for ambiance (like the blood puddle above), floor traps, etc.
Spikes are a nice example of some composite behavior. They include a floor entity for the spike chamber, and a billboard entity which receives a toggle signal (much like the previously mentioned doors). To drive the interval, I added a "timer" component which sends a signal every
N milliseconds to a target entity (in this case, itself).
There's a draw ordering bug when the spike retreats into its chamber, but overall I'm happy with the effect.