The problem
For Software Engineering Projects 3 and 4, our team of 8 had to make a game on an engine we built ourselves. We made Hook Spire: you aim the hook with the mouse, launch it and get pulled to where it lands, then jump or swing past obstacles. Since the hook is how you get around, the whole game depends on how that movement feels.
My job was split between programming and design: I built engine tools and gameplay pieces, designed parts of the level 3 and 4 sub-levels, and playtested to make the levels and the game feel better.
The constraint
- No ready-made engine. There was no Unity or Unreal underneath. We wrote dreamEngine ourselves in C++20 and OpenGL: its entity-component system, rendering, physics, input, resources and saving, and the editor the levels were built in.
- Eight people, one codebase. Each of us championed one part of the engine; mine was debugging tools. Every change had to fit around the work of seven other people.
What I tried
I worked across the engine, the editor and the game:
- The event system. Instead of systems calling each other directly, they fire events, and whoever cares responds. A bigger system subscribes through an interface, a one-off can subscribe with a lambda, and the same dispatcher handles both. Click an entity in the editor and the hierarchy and inspector both react, without either knowing the other exists.
- The logger. It writes timestamped entries and can split them by type, so crashes and errors each go to their own file. Its crash handler catches the crash signal, writes what happened to the log, then re-raises the signal, so nothing is lost.
- The profiler. A scope timer can be dropped into any block of code: it starts timing when it's created and stops when the block ends. The profiler shows each engine system's time and share of the frame.
- Selecting and dragging. I built the editor's selection manager and drag handler. Dragging was the harder problem: the mouse works in screen coordinates and entities live in world coordinates, so I convert between the two, then lock the movement to whichever axis or mode is selected.
- Editor panels. I built parts of the hierarchy view and inspector, which stay in sync, so an entity's values update live as it moves in play mode. I also built the tile map's saving and loading, tiles that pick the right edge texture, the prefab tab, and the inspector's audio tab.
- The shooter plant. An enemy that fires on a timer with an attack animation. It comes as four prefabs, facing up, down, left or right, so we could place it however a level needed.
- Levels and playtesting. I designed parts of the level 3 and 4 sub-levels, playtested all 31 levels, gave the team feedback, and made improvements where I could.
What failed
- Late jumps didn't count. Playtesting turned up a problem with the jump. If you pressed jump a moment after running off the edge of a platform, nothing happened, because the player was no longer on the ground. It made the game feel unfair.
- The plant's first shot turned the screen white. A projectile spawned in the middle of the game had no texture or shader assigned, because the engine only set those up for things placed in a scene, when the scene loaded.
What I changed
- A coyote jump. I added a 0.12-second grace window in which the player can still jump after running off a platform. I chose the grace window over requiring the player to be on the ground, so a jump pressed a moment late still counts.
- How it works. Each physics body carries a coyote timer. While the player is on the ground, it stays full at 0.12 seconds; once they leave the ground, it counts down every frame. A jump goes through if the player is on the ground or the timer hasn't run out, and jumping empties the timer, so the grace window can't give a second jump in mid-air.
- Textures for things spawned mid-game. I made the engine load and assign the texture, animation sheet and shader for anything spawned while the game is running, the same as for things placed in a scene.
What happened
We submitted Hook Spire for the module in April 2026. It was a course submission, so it wasn't released publicly.
Looking back, three things stuck with me:
- Decoupled systems. The event system was one of the most valuable things I worked on. It taught me why each system should have its own job and not know other systems exist. I had to work through quite a few systems that were tightly coupled, and it was painful, so next time I'll build them decoupled from the start.
- Communicate earlier. A few of our problems weren't technical. Someone would assume how a system worked, build on it for a week, then find out it didn't work that way. Next time I'll push for clearer discussions with the team earlier.
- Play the levels. Playing the levels showed me bugs in my own code faster than any testing I'd done, and watching other people play brought out even more flaws. I wasn't expecting that.
Hook Spire © 2026 DigiPen Institute of Technology. All rights reserved.