With everyone quarantined at home due to Coronavirus this April's Ludum Dare had vastly more entrants than usual. A total of 1383 people entered the Compo and there were 3576 entries to the Jam. That's an absurd amount of games!
Rust is a 'systems programming language' focused on performance and safety. 'Safety' means that Rust helps you avoid certain classes of vulnerabilities and crashes. I have been using Rust for my personal work and decided now was the time to give it a shot for a gamejam.
Unity is a giant game engine, but I find it hard to get into a flow state with. There are too many knobs, features, and ways to do things. It's easy to flip a bunch of switches and have something decent, but that just doesn't mesh with my personal design flow. I prefer to work in a clean mental environment, not a complex editor.
The recommended way to work with Rust and WebAssembly (Wasm) is through a command line tool called wasm-pack. I skipped using wasm-pack and instead just used a two line bash script that would run a Rust build and then call wasm-bindgen directly to generate the web bindings.
Typical Rust build times were around 1-3 seconds while working on this project, but sometimes they'd inexplicably go up to around 10 seconds. Rust is known for slow build times, and these quick iteration times were only possible by carefully choosing tiny crates. My goal was to always have the new build ready on the web page by the time I changed to the browser window.
With only 48 hours best practices go out the window, but even still I made some early choices to help make writing new code as easy as possible. One of the key things I wanted to ensure was that if I were to declare a new variable, or load a new asset, that it would only have to be declared once. Many Rust frameworks follow a pattern a bit like the following:
On web the above isn't possible because it would prevent returning control to the browser. It's not appropriate to lock up while waiting for an asset to return from a server, so all loads are asynchronous.
The theme for this Ludum Dare was "Keep It Alive" which immediately struck me as overly morbid given the rapid spread of coronavirus. I couldn't motivate myself to create a game about death, and I nearly decided to quit the jam entirely.
Instead I looked for alternative ways to interpret the theme. As I stared out my apartment window towards San Francisco hills I thought about the way people droop their heads as they walk to and from work. It's a beautiful world, but it's tough to notice the beauty every day when the routine of life weighs heavy.
What if you played as a spirit to lift people up? You could keep their "wonder" alive. You'd play as some sort of spirit and perhaps you'd lift a commuter bicyclist up into the sky where you'd whisk them around and show them the stars.
The first thing I needed was line rendering. I ripped some code out of a prior Rust project that would generate mesh line segments by creating rectangles with circles at the ends. I wasn't sure if this heavy-handed approach to line joins would cause problems. Each individual line segment was made up of a bunch of triangles so I felt that perhaps the game would lag as too many lines appeared.
For the physics I took some math for finding the closest point on a line to the ball. If the point is inside the ball then check if the ball's velocity is moving towards the point. If the ball is moving towards the point then "bounce" the ball by pushing the opposite direction on the ball.
I was pretty surprised at how great the ball physics felt without much tuning. When I started with this design the physics felt like a big unknown for me. I felt like I might not be able to get them right within the 48 hour window, but coding the physics was actually one of the shortest features in the project.
I was pretty concerned that the ball physics would be laggy, after all the ball code was naively testing for collision against every single line segment. Once again I scribbled the screen full of lines to make the framerate drop. But it didn't drop. It took scribbling the screen full multiple times over before lag showed up. So I decided to do no optimizations at all. Another unexpected victory! Thanks Wasm and Rust!
It was becoming clear that I wasn't going to have time to draw a bicyclist, or a kid looking up in awe at the sky. This was concerning because it would be very difficult to convey the theme I was going for with more abstract imagery.
The new aesthetic jumped out to me as something that looked great and could be accomplished. It felt like it'd be a beautiful little moment to click the moon and watch it wobble and roll out of the sky. The moon could roll around and collect little circles that could represent other stars or drops of dew. The player would draw lines to guide the moon to the circles.
As I was drifting into sleep my thoughts found a hidden power in the game's design. One of my favorite things in game design is finding simple techniques that provide disproportionate value. Techniques that make people go "Huh, that's really cool", but which require little effort.
When playing back the inputs I tried to preserve the pauses I made with the mouse. This was to preserve some of the human quality of the drawings and not make playback feel too digital and inhuman. If I paused for too long I'd trim the pause to a maximum length, but that code was finicky and would sometimes pause too long even in the final version.
I started to think about how people write touching letters to others, and I began to imagine an abstract narrative of someone writing a letter to someone else who is not well. I thought about writing a letter to someone in a hospital to keep their "wonder" alive, to give them some pleasant moments. There was a duality there that by keeping someone's 'wonder' alive you may help keep their physical being alive as well. Perhaps the game could evoke both feelings of keeping humanity's wonder alive in these difficult times, but also the wonder of an individual?
A level would have handwritten notes that would say things "Remember starry nights?" accompanied by a drawing. Each note would be intended as a message to humanity and perhaps to another person. By playing as the rolling ball, sometimes moon, you'd be the force that is a reminder of these pleasant memories.
There unfortunately wouldn't be time to do art other than lines so I decided to weave more types of memories into the game. Later I added a few silly memories because I felt the game's tone needed some moments of levity.
The unfortunate flaw in this entire plan is that my handwriting is absolutely awful. I connected a drawing tablet, but even with the tablet I found myself redrawing levels over and over trying to get the writing to be legible. The game would have been notably better if I simply had better handwriting.
The game did not manage to convey the theme to most players. The theme was connected aesthetically through a unique interpretation, but was not connected mechanically. Despite not succeeding in this category I'm very glad I took design risks in my interpretation of the theme.
Because I spent so long on the level design tools the process of making them was fun and fluid. The game was designed to be long enough to be satisfying, but not so long people that people would stop playing partway through.
Because of the theming of the game I tried to design the game to be pretty easy. It would be bad if the player were challenged to the point of frustration and left annoyed instead of a little happier. In retrospect I overdid it a bit with the easy difficulty. My hope is that players would find fun through creatively using the mechanics, but I could have designed levels to push them to be more creative.
I borrowed a microphone and made wind sounds with my mouth to serve as background ambience. For the star collection sound I clinked together every object and glass in the kitchen until I found the sound that was most like a star twinkling. Then I pitch shifted the sound up a bit to feel a little more magical.
The collection sounds felt far too sterile when they were all the same note. I tried randomly shifting the pitch but then a series of quick collections felt like discordant chaos. So what I did is pitch shift the sounds up or down based on the vertical position of the collectible in the level. This had the nice effect of playing pleasant scales as the ball collects a series of notes. A tiny bit of randomness was left in so that a quick series of ball collections wouldn't sound too similar.
The objective of Rust is to survive in the wilderness using gathered or stolen materials. Players must successfully manage their hunger, thirst, and health, or risk dying. Despite the presence of hostile animals such as bears and wolves, the primary threat to the player is other players due to the game being solely multiplayer. Combat is accomplished through firearms and various weapons, such as bows. In addition, vehicles controlled by non-player characters will occasionally roam, attacking armed players. Rust features crafting, though initially limited until the discovery of specific items in the game's open world. To stay protected, players must build bases or join clans to improve their chance of survival. Raiding is a major aspect of Rust. Rust supports modded servers which can add additional content.
Rust was first released in December 2013 to the Steam Early Access program. During this period of development, the gameplay was changed significantly. Dangerous wildlife replaced zombies as the primary environmental threat and several fundamental revisions to the crafting system were released, along with general improvements and feature additions. While in Early Access, Rust was ported to the Unity 5 game engine, providing substantial graphical changes. The game also introduced immutable, predetermined skin colour and biological sex tied to players' Steam account details. Despite being fully released, the game continues to receive updates.
d3342ee215