Skip to content

Devtober 2019 Post-Mortem

I’ve been talking about re-making a game I originally made in February 2015, called Gravity. This was one of my first games, after a few experiences with Ludum Dare; I decided to play to my strengths & create a platformer with no art to speak of (just different-colored squares) and to write the whole thing using vanilla JavaScript and the HTML5 canvas API.

It was…mostly a success. The game worked, but my code organization was severely lacking & I ended up with a pernicious bug that I was never able to track down. Still, I learned how to make buttons & scene management, and learned from my failures to make better-organized games.

I’ve said I wanted to try re-making the game with the lessons I’ve learned, but I didn’t really want to use JavaScript for it. The HTML5 canvas is nice, but one piece of feedback I got on the original Gravity was that people wanted the camera to rotate as you rotated the direction of gravity (since your input reference frame was tied to that rotation, players were confused when they had to press left to move to the right side of the screen when they were upside-down). I didn’t want to mess with rotations and resolution-independent scaling, so I elected to use MonoGame. I had a fair bit of experience with XNA, back when the XBLA was a thing, so I was familiar with the platform, and it would let me port between platforms fairly easily. Planning for the future and all that.

There were a few things I felt needed to be updated & improved along the way:

Controller Support

I either didn’t know about the Gamepad API, or I didn’t want to bother with it when I made the original game. Mouse and keyboard are enough for a web game, but I personally like using a controller to play platformers, and a polished release would have that functionality.

Graphics & Animations

An issue many players had in the original game was that they lost track of where gravity was pointing. My solution was to add an actual sprite for the player, rather than a simple white square; however the player sprite was oriented would be the direction of gravity. I think this was how I first had the idea to make the player a cat; it would make sense that they would always land on their feet.

Music & SFX

The original game had sound effects from BFXR. There were eight of them, with no variations, and they started to annoy players greatly. At the time I had no experience with sound design, but I knew I wanted fancier audio for the re-make. I also wanted to add music, something I also had no experience with when the game was first released. I selected FMOD as my audio middleware.

Worlds, Not Levels

One problem with the camera rotation I’d encountered in the original game is that screen bounds were no longer static. As the camera rotated, tiles moved away from the edge of the screen and showed a sad-looking black background I wasn’t happy with. The solution I moved to was a Metroidvania-like system where players could move back and forth between levels; these levels were all connected via a world map, and so when out-of-bounds areas appeared due to camera rotation, they would be sneak previews of future levels.

Unfortunately, that brought with it its own set of problems…

What Went Wrong

I’ll start with the problems I faced during the month. Some of them are a bit obvious in retrospect…

I’m Not An Artist

The original game has basic color blocks. The character is a square. There’s no animation. All these things had to be designed and added…and I’m not a good artist. I found myself designing and re-designing the player character, the character’s animations, the pickups, animating the pickups, designing particle effects…the list goes on.

I also accidentally over-sized the player character at first; I spent a while panicking about that before a quick nearest-neighbor downscale looked alright with a little bit of tweaking. Of course, the character design got completely overhauled a week or two after that…

Confusing Function Names

I haven’t used XNA or MonoGame in a few years, so when I saw functions named “Initialize” and “LoadContent” I assumed, surely content would be loaded before initialization.

This was, as I’m sure you’ve guessed given the section title, incorrect. I ended up trying to use FMOD to load audio before it was initialized, leading to hours of increasingly puzzled debugging.

Art Direction Is Hard

Who’d have thought, right? As I said, the original game had no art, music, or setting. So figuring out what tone to go for in any of the artistic parts of the game was (and is) a real time-sink. I keep waffling between a sci-fi flavor and a fantasy one, and I had absolutely no idea what style of music to use for the game. I made about 10 different tracks as experimentation, and I got increasingly distressed as music that sounded great absolutely failed to work for the actual game I was composing it for.

Cameras Are Hard

Cameras are really, really hard. Especially when they have changing boundaries. I know exactly why I had a static camera in the original game, and I still wish I could get away with it. I had issues with clipping rectangles, level boundaries, and pretty much everything camera-related that could go wrong did.

The end result is nice, but it took a much longer time to get there than I expected.

UI Layout

UI is a massive time-sink. I learned a lot, but there’s a lot that goes into a decent UI framework. I tried integrating Ultralight, but they don’t have C# bindings yet & I didn’t feel like sinking multiple days into working through interop. Of course, someone made an Ultralight/MonoGame interop project on October 27th, but that was a little late for me 😒

Unfortunately for my mental state, doing UI from scratch meant that I was often trying to architect and code the framework as I went, and had to think in terms of how both entire screen layouts and individual widgets would work. It’s still not perfect (proper layout for widgets that require constant aspect ratios is not…entirely there yet), but I was able to get a framework I can work with at the unfortunate cost of several days of dev time.

Game Design Changes

The original game had individual levels. The camera never moved, so the player had to navigate around an entirely-visible level, getting to a set “end goal” block using a limited number of gravity shifts. Levels felt like they could be both puzzles and execution challenges – execution challenges being exemplified by the level I affectionately nicknamed “Hell”:

A screen recording of a challenging level, a maze of red blocks that will kill the player if they ever touch the ground. The player must rotate gravity to keep themselves in the air without ever touching ground.
This level usually gets people to hate the death noise.

I was able to separate the levels completely, so I knew the player would always start a level with gravity pointing down, and could give the player a limited number of gravity rotations to force them to think critically about when they used their shifts.

But given the move to a metroidvania-style layout, I lost many of those guarantees. If I gave each room a specified number of gravity shifts, players could simply cross room transitions to give themselves regenerating gravity shifts & start a room in whatever orientation they wanted. If I gave rooms limited gravity shifts that, once used, only regenerated on player death – players could cross room transitions to give themselves checkpoints…and then intentionally die, again giving them whatever orientation they wanted. If rooms had limited gravity shifts that did not regenerate, players could be soft-locked into rooms by blowing through their gravity shifts.

I’m still not certain about how to solve this problem without moving back to the (in my opinion, more boring) level structure. A lot of level design decisions hinge on this mechanic, though, which meant that I was reluctant to design too many levels that would later have to be retrofitted to the new design.

What Went Right

On a happier note, I think a lot went well! I’ve got a game I’m happy to keep developing, and it’s a lot better organized than the original. Here’s a few specific things I think went really well:

So many GIFs

One of the first things I added to the engine was the ability to record the last 5 or so seconds of gameplay as a GIF and export it. I kept all the GIFs from the month, leading to some very visible progress – which was amazing for keeping me motivated. It was also helpful for motivating me to write Twitter posts about my progress to show off the GIFs!

I went from this…

…to this!

Engine Development

I’ve used C# with XNA/MonoGame a few times in the past, and I’ve gotten familiar with engine architecture. This meant that I could whip out the bones of an Entity/Component-based engine in MonoGame within a day or two. I kept adding code to the engine as I realized it was needed, leading to a lot of useful & reusable code being added. Future projects will be able to use the engine wholesale and save a ton of development time.

Design Carryover

A lot of the game was already designed, so most of that work could be carried over. Player movement, many of the pickups, and so on were all pulled wholesale from the original game. I didn’t have to worry too much about coming up with unique mechanics, as most of them were already present in the game.

In-Game Editor

This was also present in the original game, although in a much less useful form. In the original game, you could press the “e” key to bring up a level editor for the current level; different tiles could be placed by pressing a number key and clicking. However, these keys were unlabeled and hard to remember (Is 4 a blue teleporter? Better click and find out! Oh, it’s the purple teleporter. Whoops). When a level was complete, you had to press Ctrl+C to copy the JSON serialization, then edit the source code to add or replace the level (since levels were hard-coded into the game).

The new editor uses Dear IMGUI, which allowed me to not only include a world editor but also include tilesets, drop-downs, and labeled buttons. I also serialize to data files and update the current game memory on save, so there’s no reload required: just edit a level, save, and play. It’s seamless, and because the level editor is built into the game engine, any changes I make to level data are immediately shared between editor and game. No worrying about synchronization!

I also have a debug console, so I can jump to editing a specific level or world. This feature alone made level design take significantly less time.

Audio & FMOD Integration

FMOD integration went fairly smoothly. I’d tried to integrate FMOD into a Unity project a while back, which failed with incredibly obtuse error messages. This time, it went without a hitch. I would have liked better documentation for C# integration, since I did spend a bit of time puzzling out a few features.

Making adaptive music is really fun, & FMOD Studio integration made experimentation a breeze. I could include random selections of tracks, randomized EQ for sound effects, and more – all of which made the sounds far less annoying than they were in the original game.

I also wrote a few tracks that I’m proud of (even if I realized they didn’t fit the game):

Chevy Ray’s Pixel Fonts

Chevy Ray’s pixel fonts are incredible. That’s it, that’s the tweet. Check them out at http://pixel-fonts.com/.

SDF Font Rendering

I was hesitant to include SDF font rendering, thinking the implementation would be very confusing. Eventually I bit the bullet, and found that not only does it make a lot of text-based UI work easier, I could also implement it in a very productive evening. If you’re using something like Unity, it probably includes this functionality, but if you’re doing any custom text rendering, I highly recommend giving this a shot.

In Conclusion

Overall, I count this month as a total success. I didn’t work on the game every day (I took a two-day trip up to Vancouver for Overflow #007, which was incredible), but I think 29/31 is pretty damn good. I also didn’t get too burned out – probably because I could bounce around different tasks that needed doing. Making music, sound effects, animations, and code are all very different jobs & I could pick and choose which task to tackle every day based on what I wanted to do.

I plan to keep working on the game, though with a lot less emphasis on trying to have continual visual progress. Posting on Twitter regularly was fun, but I feel like it led me to neglect some of the core, non-obvious jobs (like design & direction). I’ll release a demo of the Devtober build in a few days, once I’ve got a pause menu to let you quit the game. That’s probably an important quality-of-life feature…

Congrats to everyone else who participated, and I look forward to doing the whole thing again next year!

Leave a Reply

Your email address will not be published. Required fields are marked *