Post-Mortem Ludum Dare 43

Outside my home office window, fluffy flakes of snow drifted down from the night sky, coating the ground in white, hiding the dreary brownish grass. It was a picturesque scene, and I allowed myself a brief moment to enjoy it; but then my focus turned back to the task before me: crafting the finishing touches of a sample game UI. A last practice project, in preparation for the challenging journey I was about to undertake.

In less than an hour, Ludum Dare 43 — a video game competition wherein participants design, develop, and deploy a video game in 72 hours — would begin, and I wanted to be sure my mind was primed and ready to roll the moment the competition began.

Before you continue further, dear reader, let me forewarn you: this is no mere post, simply detailing the development of a video game. This is a tale of hopes and dreams, of fear and despair; a tale of lessons learned, best laid plans, and desperate decisions; perhaps most of all, it is a tale of one man’s journey to stare dread fate in the eye and dare to succeed.

This, then, is the tale of my experience creating Sanity Wars for Ludum Dare 43, in all its horror and glory. Sit down, buckle up, and hold on.

The Beginning

I’d reserved today — November 30th, a Friday — and the following Monday and Tuesday off on PTO, in preparation for this weekend competition. I’d spent nearly a year teaching myself how to create video games, and now was when I felt my skills were sufficiently advanced enough to tackle a real challenge: Ludum Dare, a legendary video game competition where participants are given 72 hours to create a brand-new video game.

Though unconventional, I planned to enter the contest with a custom JavaScript-based engine that I coded myself, from its humble beginnings as a tutorial engine to its current state, with my unique experiments and needs added to it. It was general enough that I felt comfortable putting it to the test in the fires of competition.

This was an important moment for me. One of my dreams has been to create and release a professional video game, and to me this contest felt like the perfect opportunity to test not only my skill, but also my resolve. How would it feel to channel my creative and intellectual effort into making a video game? Would I enjoy the process enough to commit to wanting to make a full-fledged game later on down the road? Could I make a game that people would enjoy playing? Over the next three days, I reckoned, I’d find out the answers to these questions.

Let the Games Begin! (Friday Night)

At 8 p.m. CDT, the theme for Ludum Dare 43, voted on by its participants, was revealed: “Sacrifices Must Be Made”. I was pleased; this was one of the options I’d been voting for. As a creator, I’ve always been partial to the thematic and narrative drama sacrifice offers, and I knew I’d be able to come up with something that would fit this theme.

Pacing back and forth in my home office to get my creative juices flowing, I hashed through various different possibilities. Eventually, I settled on a general idea for the signature game mechanic: a side-scrolling platformer where the player used a resource called “sanity” to cast spells — but their sanity was also their health bar, and casting too many spells would deplete their sanity below zero, causing the player to die.

Additionally, players would not be able to access these spells at the start of the game; they’d instead start with something called a “sanity buff” which increased sanity recharge rate. If the player chose to sacrifice these buffs, they’d gain access to spells, but also decrease the rate at which their sanity recharged.

The narrative theme I created to support this mechanic was that the player would be fighting a malevolent being named the Dread Overlord, who held the entire world under the sway of terror, and that the player’s narrative goal would be to weaken the Dread Overlord’s iron grip, bringing salvation to the world. In this, the game also served as a loose allegory to the struggle with bipolar disorder, a struggle I am intimately familiar with, and that further endeared me to the idea. It was, I felt, a perfect fit with the sacrificial nature of the theme, as well as the sanity-casting and buff-sacrificing mechanics.

Little did I know of the irony this narrative theme would present later on in the competition.

With my initial planning made, I sat at my desk and immediately began prototyping my sanity-casting mechanic. It so happened that I already had a spell-casting mechanic built from a previous experiment. Using this as a base, I managed to hammer out a working prototype of the mechanic by early morning. The player’s hit points, or HP, would be directly tied to their mana — or, as I was calling it, “sanity”. Spells could not be accessed until their hotkey was held down for a long-enough period of time — aka a “sacrificed” sanity buff. Reducing sanity to below 0 would trigger the player death state, though I had yet to code what that actually looked like.

I also came up with a tentative title for the game: “Sanity Wars”. It wasn’t a perfect title, but I’d spent half an hour brainstorming possible titles, and this was the one that felt the least cheesy. I figured I’d revisit it later and come up with something better, near the end of the competition.

Feeling satisfied with the current state of things, I decided to call it a night and get some sleep. My confidence at this point felt solid; I believed that I had enough time to take this vision I’d come up with and hammer it out. Soon, I was fast asleep.

Time Management Issues (Saturday)

Around 8-9 a.m. CST, I woke up to a breakfast prepared by my wonderful, amazing wife, and I enjoyed the family meal before returning to the office to begin the day’s work on my game. The first thing I did was to finish implementing player death, which didn’t take much time. I just tied the player HP stat to mirror the sanity level.

Now that I felt I had the “core” of the game implemented, I thought about what to tackle next. I’d noticed the tileset I’d been working with included sloped tiles, but my engine didn’t have support for moving on a sloped tile. So…why not knock that out real quick?

Three hours later, I had a very buggy implementation of slopes; try as I might, I simply could not get the physics of walking on the slope to work correctly. At that point, I finally asked myself the question that I should have asked myself before I started working on slopes: Is this a feature I really need in my game?

The answer was glaringly obvious: No, it did not. I had been developing just fine with block tiles and jumping around, and while I loved the idea of including slopes, they were far from essential for my game to work. Thus…I nixed the idea and moved on. Three hours, down the drain, over a needless feature…

Lesson Learned: Question whether the feature you’re planning to add is necessary before writing code for it.

I decided my next goal should be to implement an enemy. Hitherto, all I had was a player and the sanity mechanic for casting spells. There needed to be at least one enemy, ideally more, for the player to contend with.

I decided I wanted to create a flying enemy, to avoid having to deal with determining where a ground character could move. After perusing OpenGameArt.com for a considerable period of time, I found a floating eyeball animation that looked delightfully menacing. With that as my art base, I created a floating eyeball entity, which would fly at the player, then back away to a random distance once they got too close, then either wander around for a bit or immediately descend upon the player again. When they were close enough to the player, they would trigger a sanity drain, thus “attacking” the player. It took another few hours, but it was complete and I had it integrated into Tiled (the tilemap editor I was using), so I could place floating eyeballs anywhere I wanted.

By then, the hour was getting late. I took a short break, to ponder my next moves. That was when it finally dawned on me: I had a mechanic, I had a player, I had an enemy…but I had no core game loop. In other words, I had no goal for the player to attain, no objectives to attain along the way. All I had was a map with a sanity caster and a few floating eyeballs. That wasn’t a game, that was a setup.

What was the player’s goal? In order for my game to truly be a game, I needed to answer that question, fast, and then implement the bare minimum necessary to make that goal playable.

Lesson Learned: Hash out the core loop for the game right away. Maybe it will change later, but at least have an initial iteration.

After some brainstorming — I can’t recall exactly how long — I settled on the idea that the player needed to survive and find a Final Exit, but this wouldn’t be considered a true victory unless the player first found some Objective; both the Final Exit and the Objective would be randomly spawned in one of a series of maps. Each map would be connected linearly by portals. I felt these things should all be doable within a day or less, and now that it was past 1 a.m. of the next morning I decided to go to bed now. Hopefully, this would give me the energy needed to bang things out quickly.

Making the World (Sunday)

The next morning, I woke up, scarfed down a quick breakfast, then headed back into my home office to start implementing the Map Portals mechanic. Up until now, I had been working with isolated test maps. In theory, I should be able to write code which would create two portals on a map, each linking to a different map, as determined by the load order in my map configuration file.

A lot of the spawning logic I used for the player also applied to spawning Map Portals, so I made use of copy-pasta and removed the map corner restriction. I then needed to add logic to prevent map portals from spawning on top of each other (or the player), as well as logic to prevent portals from spawning too close to each other.

Another several hours later, I had the portal spawns complete. Now, for the challenging part: enabling the player to press a key in front of one of these portals, and be teleported to the correct corresponding portal on the linked map.

I spontaneously decided that I wanted the player to be spawned randomly as well, and that furthermore I wanted the player to spawn only in a map corner. Thinking that this shouldn’t be too hard, I got to work designing and implementing the code that would let me do this.

It took more work than I thought it would. I not only needed to restrict where on the start map a player could spawn, I also needed to check and make sure the spawn location was actually habitable by the player (in other words, not inside a solid tile, like a wall) and directly above a ground area (so the player wouldn’t spawn at the top of a tall map in mid-air). After several hours, I had the mechanic working, and proceeded to work on the Map Portals.

It turned out to be even more complicated than I expected. First, I had to set up a system by which two portal objects could be “linked” together so that they’d always send the player to the correct location. Then I had to load the next map’s data into the game while swapping out the old map data (but not deleting it, because I’d need to restore it when the player returned to that map). The player also needed to be teleported to the receiving portal’s coordinates, which involved resetting the camera to focus immediately on the receiving portal’s location and preventing the player from accidentally teleporting back to their original location by holding the action key a fraction of a second too long. There also needed to be logic to determine where the Final Exit would be spawned, and only render the object when the player was on the chosen map. Finally, any additional entities (enemies, the Objective) on one map needed to be removed from the game loop, then put back in when the player returned to that map.

In short, I needed to make maps containing bad guys, portals, tomes, and the exit.

I spent the entire rest of the day just implementing all this logic. Along the way, I decided, instead of a single Objective, to create multiple Objective pickups (which I ultimately decided to call Tomes, giving them a bookish sprite), spawning one on each map, and that the player had to collect all of them to get the “best” ending; anything less would result in achieving a subpar ending. I also wound up having to write a custom events handler to run dispatches at the end of the current rendering cycle as part of making these portals work, as well as a WorldMap handler specifically to handle the metadata outside of each individual map.

Working feverishly, hour after hour, I finally had everything functioning as intended, except for enemy persistence. By now, it was nearly two in the morning. I had intended on composing an original song for this game, but some part of me warned myself that I might not have time for that on the final day, so I listened to a few of my older compositions and picked the best-fitting one as my backup soundtrack.

Lesson Learned: Things will take longer than expected, so account for that when planning.

Utterly exhausted, I collapsed into bed and tried to fall asleep. I tossed and turned, and I growled mentally at my body for being so stubborn…but sleep continued to evade me. At that point, thoughts flooded into my mind about how every minute I spent awake in bed would lead to one less minute of sleep I’d get, because I absolutely could not afford to sleep in late this time, like I did the other days.

After hours of this dreadful torture, I did finally fall asleep, but not for very long.

Dread Realizations (Monday)

At 8:00 a.m., the alarm blared and yanked me out of my fitful slumber. Compared to the other days, my mind was besot with grog and lack of clarity from the get-go. I plodded to the fridge, grabbed an energy drink, and started downing it. I had no time to think, no time to reflect; I had to finish enemy persistence, and whatever other little things I’d forgotten, to at least get the game to a playable state.

I thrust myself into the chair before my laptop and got to work. In another couple of hours, I got the enemies to correctly persist across maps. I also implemented a maximum enemy amount per map, and a rate which enemies would spawn/respawn into the map. I also threw in some simple ending screens to test the end-game conditions.

It was past noon by the time I got all this working. Meanwhile, my wife was playtesting builds of the game on another machine, and I would need to run back out there multiple times and pull down the latest builds for her to test with.

At this point, with the core loop finally in place, I decided to start looking around for final art assets. The ones I’d been testing with weren’t bad, but they didn’t quite fit together, and it bugged me enough that I chose to divert time into searching for new assets. Hours passed, and before I knew it the time was 4:00 p.m., and all I had to show for it was a single tileset and new sprites for the character.

The deadline was at 8:00 p.m. In short, I now had four hours to create the actual levels, create music, create sound effects, add story text and cinematics, upload the game to a server, test everything…that’s when it finally hit me: I’m not going to be able to release my full envisioning of the game.

It was at this point that my bipolar symptoms, hitherto under control, started to flare up strongly. Dark thoughts filled my mind about how badly I’d executed this game, how unlikely it was that I would even be able to get it finished. Every tiny little mistake I’d made, from the botched slopes to the overly-long search for a new tileset, flooded into my mind.

“You’re not good enough for this,” my brain whispered to me. “You did your best, but it wasn’t good enough and you’re going to fail.”

I kept trying to push forward with making levels. Nearly an hour later, I only had a single map that looked somewhat decent, and my mental anguish only intensified with each passing minute. There were now entire minutes where I’d find myself paralyzed with derision, my mind crushed under the anguishing weight of despair, my body unwilling to even move a single muscle. It had been a long, long time since I’d felt such intense levels of depression, and this scared me most of all. If this is what it’s going to be like every time, how can I justify making games in the future?

Sanity Wars, indeed. The imagined narrative of my game — of which I had yet to even write a single line of text — proved utterly ironic as the villain of my game threatened to consume the game’s creator.

Somehow, despite mostly working in fits and spurts, I kept forcing myself onward, even as my depression screamed at me about the futility of such gestures. My wife helped out by creating a map of her own, and I threw together a few additional barebones maps with a few randomly-drawn platforms. I found a few open-source sound effects to combine with my sfxr-generated effects I’d already put into the game, and I also took the backup music I’d selected the night before and added it as the game’s soundtrack.

Somehow, with less than two hours until the deadline, I wound up with a playable game. It was nowhere near the game I’d envisioned it to be when I started the competition; compared to that, it was abhorrent, abominable, awful garbage. Yet…it was still a game, and it was playable.

The Dread Overlord was still doing his best to crush my mentality and inconvenience my efforts, but, nevertheless, I slogged on. At this point, I had steeled my resolve. It was going to be a crap game, it wasn’t going to capture the theme nearly as well as I’d planned, and it was surely not going to do well…but I was going to finish and release it, flaws be damned!

The Final Push (Monday Night)

Now that I had something to release, it was time to ensure that the game did get released. Although it felt a little early–I still had an hour and a half of time–I decided to go ahead and set up the release platform for my game, so I wouldn’t be scrambling to do it at the literal final hour. As this was a web-based game, my plan was to host this as an AWS S3 website, where I wouldn’t have to deal with setting up and hosting a server, or learning some other company’s setup for their own hosting service. My decision to tackle this now would prove fortunate.

I mentioned previously that I’d built my game-engine based on a tutorial book. Said tutorial book gave instructions on how to set up a dev server called Budo for use with the testing environment…but, it turned out, had not provided any instruction on how to actually deploy the finished app to production. Scrambling, I rapid-researched how Budo ran under the hood, discovered it was using Browserify, and figured out how to set up Browserify to deploy a production build of my game script. After a few other snafus, I managed to correctly set up a deployment script.

Now it was time to create the S3 bucket-site and copy my production assets into it. In my depression-paralyzed state, I screwed up my first deployment attempt and spent almost half an hour trying to fix my AWS permissions before finally opting to blast the first site and make a new one. The second time through, I set the configuration correctly, and the deployment worked as intended. With less than an hour to go before the deadline, I at least knew I could reliably deploy the game.

I used every minute of that last hour to add as much polish as I dared get away with. By now, the adrenaline of deadline crunch was overcoming the dread weight of my depression, so I was singularly focused on trying to make my game at least not absolutely terrible. I converted my test ending screens into actual ending screens, so that I could at least get some of the story’s context into the game. My wife helped by writing the actual dialogue for the ending scenes, while I crafted the introductory scene establishing the game’s tone. We got the final words in, and deployed, at literally the last possible second, at 8:00 p.m.

At the end of the deadline, Ludum Dare allowed for a single hour to get the game deployed, and to fix any last-minute bugs that come up during this process. I honestly don’t recall much of what I did at this time, other than fixing a few things. At the end of it, I belatedly marked the game as “Unfinished” as I crafted the submission post. It only seemed fair, with the game in a barely-publishable state, to mark it as such. When I made my submission post, however, another fellow Ludum Dare-r messaged me, advising me to mark the game as “Jam” instead of “Unfinished”, pointing out that hardly anyone was going to play a marked-unfinished game, and I thus wouldn’t get any feedback on where I could improve. Deciding that I could at least treat this as an improvement opportunity, I took the person’s advice and changed my submission type to “Jam”.

Shortly after I did this, 9:00 p.m. hit. The competition was truly over, and now no changes were allowed. I collapsed onto the living room couch, utterly exhausted and in a foul state of mind. My wife tried to cheer me up, to focus on the fact that I had actually finished and released the game. I wasn’t quite ready to rejoice over that fact, especially since I found myself dreading the horrible reviews which would surely come.

But she was right: I had finished the game. It was a poor excuse of a game, in my mind, but I had actually done it. I’d built a game in three days, and on my first-ever attempt at such a feat. It wasn’t at the standard of quality that I hold myself to, but it was something.

Ultimately, I opted to push all thoughts of self-review and criticism out of my mind until I had gotten a good night’s rest. Fumbling my way through my regular nightly routines, I fell asleep mere minutes after tumbling onto the bed. It was a deep, deep slumber.

The Judgment

I didn’t get out of bed until nearly 10:00 a.m. the next morning. Immediately I noticed an improvement in my mood; sleep had seemed to help tremendously. At the least, I could finally acknowledge to myself that, yes, I had finished a game, and take some pride in that fact.

Lesson Learned: Never underestimate the value of good sleep. Working rested makes a huge difference not only in how you feel, but in the quality of work you output.

Now that the development part of the competition was over, each participant in the jam was expected to play each other’s games, rate them, and provide feedback. I hopped onto my desktop and started checking out the various games. I played a lot of clever implementations of the sacrifice theme: as a game mechanic, as part of a narrative, as the goal for the game…the creativity from the other developers was on full display.

All the while, I kept checking back on my own game. To my surprise, people were not only playing the game…they didn’t completely hate it. Sure, there were critiques on numerous aspects of the game, many of which I was expecting; but there were also plenty of positive comments about some things which they thought I’d done well: the choice of game genre, the incorporation of spellcasting, the visuals and audio… I hadn’t been expecting anything positive, so these comments pleasantly surprised me.

Over the course of the month of December, I continued to play other games (though not as many as I’d have liked, due to work obligations and the holiday season) and give my own ratings and feedback. Other people continued to play my own game and leave their feedback.

At last, at the beginning of January, the ratings period was officially over, and the scores were released for every game. I logged onto the site, and found my results:

The final results from my debut game entry, Sanity Wars.

With my own numbers known, I went to check on the overall stats for the jam, from which I’d learn how high my score truly was:

The overall statistics from Ludum Dare 43.

There were 2,514 submissions. Of these, 1,494 had received enough ratings (20 or more) to be awarded an official ranking.

Thus, my final placement: 842nd out of 1,494. Right in the middle of the pack.

To me, the final score was both surprising and expected. Expected, because this was within the middling range where I felt I’d wind up with my first-ever game entry. Surprised, because I never expected this flawed, imperfect game to score so highly. It still amazes me even now, as I type this retrospective.

It just goes to show: Never give up, even when all hope seems lost. This adage is a mantra that I try to live by, contending with my bipolar disorder, and time and time again I find it to be a true maxim. With my emotions screaming failure, I persevered and found triumph.

I Am Now a Video Game Developer

It’s official, now. Having designed, built, and released a video game, one which people were able to play, I am now a video game developer. It feels good to say that, after all the learning and preparation I put in to get to this point.

Overall, this competition was an incredible experience for me, in many ways. I set out to make my first-ever game, and I not only succeeded, I did better than I expected of my entry. Though I still wish I had been able to release a more polished game, the experience I’ve gained was invaluable, and will prove critical to my future endeavors to publish a full-fledged video game.

Among the things I learned: coding a game engine from scratch is hard. I mean, I knew that, and I knew that the main reason I did build an engine was to learn how things worked from a general perspective, but it gave me a whole new appreciation for the work game engine developers put in to make usable, reliable engines for others to use. Although I like tinkering with systems and how things work under the hood, Sanity Wars drove home a well-worn adage in the games industry: make games, not engines.

I also think I tried too hard to come up with a complex narrative for a game that, ultimately, did not need much narrative involvement. Presenting an allegory of the bipolar struggle may be neat for short stories, but it didn’t translate well into a game. I’m a narrative-oriented person, so I like creating complex narratives; however, by focusing too hard on cleverly implementing sacrifice in Sanity Wars‘ narrative, I instead sacrificed the fun of the game mechanics. In the end, I felt neither the narrative nor the spell sacrifice was well-implemented, and I could have done both more simply by highlighting the sacrificial nature of tying spellcasting to your health.

My goal with taking part in Ludum Dare was to gain experience, and in that regard I achieved success. Most of all, I learned that I can, indeed, create video games, and that this is something I want to keep doing, depression be damned

To put it in a melodramatic way…I faced the Dread Overlord, and conquered him

What’s Next?

I am currently working on rebuilding Sanity Wars in Godot, an open-source game engine, and comparing the experience to coding everything by hand. I suspect it might ease many of the pain points I had developing my game and engine.

Why did I choose Godot, over something more familiar to my background, such as PhaserJS? Well, I read plenty of good things about its ease of use, and the node-based architecture is very similar to the structure I used in my own engine. Plus, I thought it’d be nice to have a GUI to work with, instead of coding everything by hand. I plan on releasing the Godot version of Sanity Wars once I’ve finished the port.

The next Ludum Dare is at the end of April. I’ve already made sure to take time off for the dates of that contest. I look forward to taking on whatever challenges it throws my way, with the goal of releasing a more polished game.

As for my dream of making a professional video game? That dream is alive and well, and as I continue to learn and grow my skills, my confidence that this dream can be realized continues to grow. And as long as I keep moving forward, and don’t give up, I have no doubt that one day this will no longer be a dream.

It will be reality.

You can play the Ludum Dare version of Sanity Wars here.

No Comments

Post a Comment