I recently took a stab at developing my bucket list video game. My goal was to create an infinite asteroid field optimized to be addictive. I successfully built an infinite asteroid field in the browser, but I’m still trying to find my audience. You can play! It’s hosted at https://shmup.ledbetter.dev.
Since I was young, I’ve had a love for classic style video games. Among my outstanding influences were Solar Winds, The Ur-Quan Masters, Sinistar, xpilot and of course the original Asteroids and Spacewar! In my opinion, there’s really nothing like these classics in modern gaming. The Ur-Quan master in particular was amazing in its ability to create a universe, complete with warring alien species which seems to exist completely independently of the player. Of course, these kinds of games always leave the player wanting more.Myself, I have always wished the world maps could extend to infinity, with realistic solar systems, procedurally generated NPC civilizations and be massively multiplayer.
I first tried to build such a game in middle school. It was pretty good, I got the basic mechanics of being able to fly a ship around and shooting missiles, despite having only a vague understanding of trigonometry and physics. I got lost in the multiplayer networking.
While studying for my undergraduate in physics, I tried again. This time one of my main goals was to put my little triangle in a realistic orbit around a planet. With my more advanced knowledge of simulating physics, I created a miniature solar system with asteroids floating around in lagrange points. I successfully accomplished my goal, but it wasn’t very fun. It turned out that orbital mechanics is hard, and not very intuitive. The few people who really enjoy such a thing are probably working at NASA. I’ve seen games focused on this mechanic recently.
After working as a professional software engineer for a few years, I developed more than sufficient skills to develop a multiplayer game engine with realistic physics. I was able to synchronize multiple users in a single game world. However, I was still too focused on creating a realistic game engine, and the result was not very fun.
Now, a few years later, I’m trying a new direction. This time I have more of a product/ML focused mindset. I had two goals:
- Create an infinite asteroid field
- Make it fun to play
This way, I’ve accomplished a little of my audacious plans, with an eye towards finding support to develop the full scope. I’ve accomplished the first goal, but I’m still trying to make it exciting enough to attract mass attention. If I could garner enough attention, I have plans in mind to optimize a fun and addictive game.
Choosing the physics engine, matter.js
Unlike in previous attempts, I’m less interested in developing a fast and efficient engine myself. I want to create an MVP which people will enjoy playing. This needed to be an easily accessible game, that I could optimize for fun. The main feature being the infinite asteroid field, which creates that feeling of freedom I so crave.
My requirements for the physics engine were:
- Deployable to an easily accessible platform
- Easy to build an MVP
- Capable of simulating an infinite asteroid field with the technique described below
The first constraint narrowed my choices to web frameworks. Early incarnations were developed in Pascal and C++ interfacing with graphics engines like SDL. At Disney I developed on Unity for a hackathon, and I’ve always wanted to do something more sophisticated with it. The problem with these platforms is they require my potential players to download and software across multiple platforms. I want to avoid that friction until I have evidence my players would put in the effort. By deploying for the web, my players can try the game within seconds.
Fortunately, there are multiple candidate web frameworks. I winnowed the candidates further by perusing their examples and source code. I looked specifically for the components I would need to create an infinite asteroid field. I already had a basic engine in mind which I describe below.
In the end, I decided to work with matter.js. This is a great physics engine if you want to develop a simple physics engine available on the web. There are issues with matter.js which would make it difficult to use as the core of a multiplayer engine, but I can work those out if the gameplay proves itself.
In the end, I definitely accomplished my goal. A new player is only a hyperlink click away from trying SHMUP for the first time.
Creating an infinite asteroid field
Frank talk time. I’m not literally simulating an infinite number of asteroids. I’m simulating a finite volume, and replacing the boundary with samples from an infinite volume. The resulting effect is to make the interior volume indistinguishable from a view of an infinite asteroid field.
This is a new approach as far as I’m aware. Although it definitely has some commonalities with procedural generated worlds. I think that the approaches could be combined. Usually, asteroid games create a simple periodic world. When asteroids or the player cross the boundary in those games, they are recycled to the opposite side. The problem with this is it’s a closed system. Where can you correctly insert a new asteroid in a periodic world? Worse, the player can easily perceive the period effects. I wanted to create a grander illusion.
Imagine a very large volume filled with interacting bodies, left alone for a very long time. This is not only a description of the infinite asteroid field, but also the basis for ensembles in statistical physics. If were to take a snapshot of an arbitrary small volume in the field. The velocity and position of all the asteroids would come from a particular distribution independent of time and space.
Really, the distribution for a very large asteroid field is a grand canonical ensemble. However, I am constraining the samples to have a specific number of asteroids, which makes it a canonical distribution. In the future I may explore using a full grand canonical ensemble.
To create the infinite field effect, every few seconds the volume just outside the camera is shuffled into a sample from the infinite distribution. Since the equilibration occurs just outside the field of view, the player never observes the equilibration in action. Furthermore, since the equilibration produces a volume identical to the outcome of leaving the volume to itself for a short time ((the relaxation time)), the player is not able to find evidence that I’m tampering with the universe.
To implement this effect, I divided the game universe into a grid of “sectors”. Every few seconds, the sectors 2 blocks away from the center of the camera, the equilibration sectors, are shuffled into an equilibrium distribution. The sectors 1 block away, the game sectors, are left untouched. If asteroids cross from the equilibration boundaries into the game sectors, they remain untouched in later equilibration cycles. Asteroids which escape beyond the equilibration sectors are removed from the universe. A constant number of asteroids are maintained in the equilibration sectors by adding or removing them as necessary.
This successfully simulates an infinite field that fools me. I can’t tell the difference. Without probing the game code, the only way a player could prove to themselves of my trickery would be to take note of an asteroid they can see, fly away, and then come back to see if it was still there. However, they can’t fly too far away, or else, in the time it took them to get back, who knows where that asteroid could have gone. If they fly just far enough that their target enters an equilibration sector, it won’t necessarily disappear, since the asteroids are only being shuffled and energetically equilibrated, which is pretty much what they would see in a really infinite field.. If they fly any farther, it shouldn’t surprising that they’ve lost track of their target completely.
Another strategy to try to find the seams in my asteroid field would be to follow an asteroid. Surely it must disappear when it reaches the game boundary? Well in this case the asteroid will remain in a game sector along with the ship, and will drift along randomly with them for as long as they need the company. So there does not appear to be any boundaries.
Controlling the ship
With the physics and and infinite asteroid field in play, I took time to tune this ship controls. My goals were the following:
- Able to see ahead at high speeds
- Able to strafe, or shoot in a different direction than motion
- Able to stop quickly
- Able to stand and shoot without worrying about momentum
- Intuitive for new gamers
From previous efforts, I know that since the screen area is limited, it’s nice to have the camera lead the ship in the direction of motion. By doing this, the player has increased opportunity to react before encountering obstacles.
In all shooting games, it’s important for strategy to be able to strafe, or shoot in a different direction than you’re moving. Usually, this is accomplished by having separate controls for moving and facing the players. However, this increases the learning curve when being introduced to the game.
After some experimentation, I realized I could accomplish both of these goals in an intuitive way by using over-damped springs. I attached an over-damped between the mouse and camera, to control motion, and between the camera and ship to create a look-ahead effect. Further, I drag the ship look direction quickly towards the angle between the camera and the mouse. Since the springs contract slowly, this gives the player time to shoot in one direction while accelerating in another, all in an intuitive way.
The result is a capable interface, handled by a single familiar and intuitive input: the mouse. I’ve never felt a more comfortable interface in the classic games I love. I can’t wait to use these controls in a dog fight with other players.
However, as awesome as I feel these controls are, I know that novice players often cling to a stand and shoot strategy. They want to sit in the center of the screen and shoot. And they shouldn’t have to struggle with momentum to do this. So I included a braking zone to make this easy.
When the player mouses within the braking region, where the ship would come to a stop, I disable all acceleration. The mouse-look still works, so the player can still aim. This enables the player to stop, stand, and shoot, without having to think about momentum.
The result is an intuitive control scheme which I’ve successfully tested with a handful of non-gamers.
Reward pickups
An important component in any fun game is being rewarded for your effort. Getting points for destroying asteroids is great, but it’s easy to go beyond to a more stimulating reward scheme.
There’s a fairly obvious possibility coming from real asteroids, and embodied in one of my favorite games, Sinistar. That is, when you destroy an asteroid, you should be rewarded with its contents. Real life asteroids contain rare minerals which could be worth a great deal of money. In Sinistar, the asteroid products can be exchanged for sini-bombs (not to be confused with cinnabons), which can be used to fight Sinistar before he eats you.
Furthermore, and this is important. Points should be dropped randomly. Not every asteroid should drop a reward. This gives the game a more endorphin stimulating quality similar to slot machines. I’d love to back this up with my own data. The best I have is anecdotal evidence from an 8-year old player tester, who bragged to me about getting over 1000 points.
Sound effects
In addition to feeling rewarded by random pickups, I felt it was key to have rewarding sounds for every action correlating with key game play. Unfortunately, I don’t have the experience or the equipment to create cool sound effects.
Instead of creating the sounds myself, I hired some pretty awesome artists from fiverr to help me. Adamrucki and naxalit did a great job and very quickly. I only wish the game was more successful so I could commission more work.
Collecting and processing events
Before advertising my game on reddit, I wanted to be ready to learn as much as I could about what players liked. There are so many parameters that affect the game’s experience, like spring constants, equilibration temperatures, reward drop probabilities, more than would be worth listing here. I didn’t want to set these parameters based only on my own preferences.
I held out length of gameplay as my main proxy for game enjoyment. To measure this, I send time stamped event beacons, containing a unique player identifier, and all the game parameters to optimize at regular intervals. I take privacy seriously, so I generate the player identifiers in a way that would not allow me to personally identifiy the players. Based on this data, I should be able to measure the impact of the parameters on the length of game sessions, and what ranges of parameters might have scared players away.
To record these events, I needed a simple backend . No other part of the game requires a backend.
My requirements for the event collection system were:
- Easy to implement MVP
- Easy to scale up
- Scalable cost down to zero
I was very excited to let people play my game, and I was hoping to see thousands of players. While I’d be happy to shell out cash to support a large fanbase, I didn’t want to end up paying anything just to leave my site up. I succeeded in this, and pay \$0 to host my game at the current activity level.
The backend architecture I chose is simple. I used an API gateway routing to AWS kinesis firehose, which just writes the events to S3. Since I intended all data operations to be global, there’s really no better database for my queries than s3. There is no need for a real-time event pipeline to update. I simply rewrite my batch processing as necessary. When I process the events, I use a jupyter notebook with s3fs and dask, which reads and processes my dataset in seconds. In the blessed state where I accumulate too much data to process easily or if I want a near real-time dashboard, I can easily attach further processing to s3 listeners.
Kinesis firehose has one perplexing limitation, allowing only 1000 records per second to be pushed. This limitation can easily be overcome using the same basic scheme as Kafka. Namely, I can create more delivery streams and write each record batch to one of the streams at random. I can only guess why firehose doesn’t do this internally.
To mitigate issues with the throughput limitation, I slowed event transmission to once every 5 seconds, and retry on errors. This would allow me to reliably collect and process events from thousands of simultaneous users without breaking a sweat.
Where to go from here?
I’m still looking for a sufficient audience to support my audacious plans. Besides optimizing my game parameters for fun, I would be very interested in taking samples of input from high scoring players to create a baseline AI. However, maybe I’m putting the cart before the horse and users would be more interested to play if I had realistic competitors. I’ve considered recording my own play, or simply adding bots with a simpler strategy.
Another issue with the audience might be the graphics. I personally love simple polygon graphics, but the customer is king. I’d love to pair up with someone with real design skills for this kind of game.
One technique I’ve learned from browsing indie gaming forums is that I can get a much clearer picture of my potential audience by posting mock previews of my game before I’ve spent effort actually building something. As an engineer, I’ve always been hyper focused on the engineering aspects of my projects. I’m still almost magnetically drawn to focusing on the sophistication and efficiency of my software. I need to continue to grow in my awareness of marketing strategy.
In any case, I had hours of fun building this iteration. So I’ll keep this game on the back burner. I never know what the future may hold.