Initial Release/Marathon Jam Postmortem


I generally never log the development for any of my work, including on this game, so this is mostly me recounting about 6 weeks of work from memory. Though I suppose this could mark the start of a new good habit going forward.

GMTK Game Jam 2021

This game was originally intended to be submitted for this year's Game Maker's Toolkit Game Jam, marking the 3rd GMTK Jam I've participated in. This year's theme was "Joined Together," which almost immediately sparked these two key concepts in my mind: attaching numerous modules to the player that grant new abilities at the cost of reduced maneuverability, and strategically detaching mods to gain a momentum boost. My original idea was a Asteroids-style space-shooter wherein the player upgrades their ship by harvesting and assimilating ship parts from defeated enemies (an idea which would be eventually realized by one of the winners of this jam...). I switched the mechanics to be a Sokoban-style puzzler because I hate programming enemy AI; I switched the aesthetics because of a design I drew while brainstorming that really made me laugh.

Unfortunately I was unable to achieve minimum viable product due to some serious bugs in the core mechanics, so I ended up submitting nothing to the jam. While I never even got to the point of creating levels, I did make large strides toward creating something  playable, including:

  • Constructing my own reusable node system for turn-based, tile-based gameplay (the last block-pusher that I made for GMTK 2020 actually used colliders and raycasts to detect neighboring objects, so...yeah...)
  • Programming the core mechanics, including all the unique interactions between objects
  • Designing sprites for all game objects (which remain mostly unchanged to this day)
  • Creating animations for each game object, which includes both the 2-frame idle animations that every object has and the more involved animations, like the mouth opening/shutting animation. In hindsight, this was a massive waste of time for a 48-hour jam, but I was intent on making my game stand out from the crowd, at least visually.

Despite not meeting the deadline, I was so invested in seeing where I could go with these mechanics that I decided that I would continue development until I felt I couldn't add anything more to enhance the game.

Time Betwixt

After a few days of post-jam sleep, all my focus was put into making this thing work. This meant a week of refactoring and de-spaghettification of the core block-pushing code. Implementing the block-pushing for this game in particular presented three major challenges:

  1. Each "block" that comprises an arm must act as one unit when it comes to pushing and being pushed, and which blocks must act as a unit can change as arms are attached and detached from the player
  2. Unlike Sokoban, the player is able to push all the blocks in a row instead of just one, so long as none are blocked in front. This combined with the multi-block units means that a single push can create complex "push trees" of blocks that need to check for a free space before any of them can move
  3. Certain actions can create two push trees that are moving in different directions. This can cause two blocks to fight over a single tile, so a protocol to manage these collisions must be laid out.

I solved this with what basically amounts to a depth-first search through the push tree which looks for a blocked tile in the direction of the push, followed by another traversal to inform each block whether they could move or not. The collision problem was solved on a case-to-case basis--though when I eventually got to designing the levels, I encountered a collision case that I overlooked, which I still haven't fixed. Once that was settled, I basically had all the mechanics that I wanted to implement, and I created the following level to test all of them:

If you recognize this level (and reached it the legit way), then major kudos to you. To be honest, I mainly added this level to the final build as padding. On retrospect, though, I suppose it's somewhat fitting as a final level since it can act as a culmination of applying all the skills you've learned up until then, even if the level is fairly easy.

Ideally I would have had about a dozen levels just like this one ready to submit for the GMTK Jam, but oh well. I had fun competing and I definitely learned a lot in that time.

Marathon Jam

By the time the Marathon Jam started, and after extensive testing, I noticed a key feature of the game's mechanics that I didn't exactly intend: it was stupid hard. So hard, in fact, that my next project goal, which was only supposed to be added as a convenience to the player, became a necessity and priority:

Undo Function

Implementing and debugging the undo/redo function took a total of around 2 weeks, but it was absolutely necessary due to the intentional un-intuitiveness of the controls (like, imagine if Stephen's Sausage Roll didn't have an undo function--Shudder). Since I wanted the undo system to be as permissive as possible (i.e. no turn limits, can rewind as far back as you need), I knew I had to make space optimizations.

My first thought was to simply record the payer's inputs each turn and have the engine reproduce the previous turn's state. While this works fine for replaying actions going forward in time, going backwards is impossible due to entropy; the system would not be able to differentiate between the player pushing a block and the player merely moving right next to the block, despite having the same input and end state.

I settled with a system similar to how repositories work: track every object that can experience any sort of state change, and each turn only record the states of objects that have actually had a state change. The state objects hold data for which turns they are active, so loading a new state is just a matter of looking up which state is active during the desired turn for each object. The system also needs a way to prevent the player filling the state stack with a bunch of "no op" states. For example, shooting a wall fifty times shouldn't fill the stack with fifty identical states.

This all sounds nice and dandy on paper, but the implementation certain had its challenges, especially since when and how the state changes are recorded is heavily dependent on how the engine works. Getting the undo function to work ended up requiring quite a bit of refactoring on the engine's part. It's still not perfect as there are cases where certain objects will record identical states when others are recording new ones. However, when trying to optimize this I ended up breaking everything and had to revert the change. Anyway, it works now, so I'm not going to touch it again until I need to.

Polish

At this point in development, any sane person would probably create some levels so when the deadline arrives, they can ensure having something to play. But not me. I don't like building levels until all the key features are implemented, and for some reason my weird brain considers sound effects and fancy animations essential.

So next on the docket was creating visual polish in the form of level transitions, animated tutorial text, and a nice looking title screen. These were all pretty straightforward to implement with smart use of Unity's Animator component. It was just time consuming because, well, art takes time. Fun fact: the "Level reset" finger twirling animation is comprised of only 6 frames and took me over 4 hours to draw, but dang does it look good. I joked to myself that I designed the levels to be as hard as they are just so the player would have to reset often, thus making ample use of the reset transition that I painstakingly animated.


Most of the sound effects were newly recorded just for this project. I tried to use primarily sounds produced by my mouth and hands to match the anatomy/cute-body-horror aesthetic of the visuals. The rest of the sounds were sampled from previous original recordings I had. No synths used here; just some light processing in Audacity.

The little music jingles were recorded straight from my MIDI keyboard, and I definitely spent more time choosing a soundfont than I did composing the tunes. They are mostly inspired by Breath of the Wild, simply because that is what I was playing at the time. I'm still not too sure if they match the overall tone of the game.

Level Design

Believe it or not, 90% of the levels were made in a single day, right before submissions were due.  My main two goals when designing the maps were:

  1. Make sure all the main mechanics/key interactions are showcased. To me, it's a waste to program all those rulesets and not have the player use all of them. More importantly though, it's the A-ha moment of discovering a new interaction that gives each level a new sense of intrigue.
  2. Don't overwhelm the player with too many new mechanics at once; try to keep it to no more than one new mechanic per level. I'm not sure I succeeded at this point. There's one moment in particular, occurring in the second half of Level 8, where the player, who has only just been introduced to door switches in the same level, has to figure out how to prop open doors without being properly guided by the level design. Also, uh, spoilers for level 8 in that last sentence.

So if each level feels rather dense, it was because I was designing with these two points in mind while also being under a time crunch. Though despite the rush and the lack of playtesting, I'm actually quite proud of the quality of the levels I was able to put out. While I would definitely love to do a deep-dive into the nitty-gritty of each level, I can't exactly show my hand just yet. Perhaps once I've put this game to rest I'll make another post detailing my thought process behind individual level design, because, man, I just love talking about this stuff.

In the meantime, I'm going to continue expanding on this game because there is just so much about this concept that I still want to explore. If you've made it all the way through the game (and/or made it all the way through this post), then you are 100% a cool person and we are officially allowed to be friends now.

Known Bugs

  • Under certain arm arrangements, releasing an arm can cause its segments to overlap with another arm's, which will cause one segment per collision to be erased from the node map, turning it intangible. I tried to design the levels to avoid this kind of interaction, but it's possible you might encounter it.
  • Audio/visual bug when an arm is jettisoned and reattached in the same turn. This is particularly noticeable in level 6.

Future Developments (i.e. what to look forward to)

  • More levels! I will also add new levels between/amongst the current ones to level out the learning curve a bit.
  • Two new key mechanics: one will mostly act as just another obstacle to get around while the other one will drastically impact the way all entities move...
  • Quality-of-life features, including a save system, level select, and settings menu
  • Maybe some BGM if I feel like it

Files

Devil's Handshake v0.1.zip 27 MB
Jul 25, 2021

Get Devil's Handshake

Download NowName your own price

Leave a comment

Log in with itch.io to leave a comment.