Slow Rush Studios logo,
    depicting an apprehensive-looking snail rushing forward

Slow Rush Studios

News index
Choosing an Engine  ▸

The Game So Far

Contents

New year, new adventure!

I'm making this thing a little more official by spinning up a website so people can follow along, so let's talk a bit about what game we're aiming to build.

What are we building?

Broforce x Magicka x Noita - a local multiplayer couch-coop 2D pixel-art platformer-lite, with a sweet magic system that lets you abuse pixel physics to kill baddies and (accidentally) your friends.

AI Mockup

(Concept art from your favourite AI; ChatGPT clearly doesn't understand what "2D" means!)

I'm going for fast-paced, chaotic and easy to pick up & play, with plenty of "woah that's sick" and "NOOoo why did you do that!". Target platform is PC & friends (no consoles). Online multiplayer would be amazing but I expect it will also make building take twice as long... so yeah, we'll see about that.

How we got there

So that's the initial plan! I haven't even prototyped most of this yet, so it'll be funny to look back in a year and see just how wrong I was.

What we've got so far

So, what's the most distinctive feature of "a coop 2D platformer with a sick magic system"?

A: The pixel physics!

Terrible jokes aside (have I mentioned I'm a dad? Apparently bad jokes are encouraged now), the pure platforming aspect should be relatively straightforward (if fiddly) to implement, and the magic system design is still noodling around in my head, so I started by hacking away on the most fun part: the falling sand simulation, or pixel physics for short.

I implemented something similar about a decade and a half ago as part of cloning Jetmen Revival; if you watch some gameplay you can see it has flowing water and destructible terrain, with each pixel simulated individually. My clone did a terrible job of implementing bouncing particles as I didn't understand trignometry properly at the time (maybe I still don't - we'll see!), but you could destroy terrain and have water flow through the gaps in it.

So I needed to re-implement that! I am writing this whole shebang in Rust (a topic for another time), and I didn't want to get too caught up in engine choices yet (also another topic), so I picked the most lightweight GPU-based rendering abstraction I could find that still let me easily set individual pixel colors: a library fittingly called Pixels.

I have no idea why I picked such an ugly shade of yellow.

With inner brutalist appeased, it was time to implement some "physics".

My first pass at bricks, sand and water technically worked, but looked.. not so great.

Bricks are orange, sand is green.. and yes, the end of the video shows sand stacking on water.

Both water and sand biased towards flowing to the right, and water had a not-so-wonderful tendency to pile up rather than disperse; these were both side effects from implementing pixel physics as a deterministic cellular automata, which is a fancy way of saying that the state of each pixel is determined by the state of the immediately surrounding pixels.

Fortunately, there is plenty of prior art around, such as Powder Toy and Sandspiel, so quite a few iterations later I got to something a bit more reasonable.

In other news, sand is yellow now.

The implementation is complicated by performance concerns: a naive implementation of simulating even a screen-full of sand (which is much cheaper to simulate than water) will already drop speeds to below 60 frames per second - and any actual game will want a larger amount of pixels than that. So when pixels move, I keep a record of the old and new positions and next frame the pixels near those get updated; it sounds simple, but I tried about 3 other approaches to reduce false-liveness and they all cause weird behavior in pixels.

Still, it's not a particularly great approach! Sometimes large swathes of water get marked inactive, and then only slowly dissipates:

The water stacking is particularly bad when you drop a huge amount near a wall.

Noita's Petri Purho gave a fantastic talk on how they implemented falling sand at scale based on dividing pixels into chunks, keeping track of live regions within those chunks, and updating chunks in separate threads; I'll have a stab at that soon. Another way might be to move simulation to the GPU and thereby brute force it, but that will make it much harder for me to iterate on gameplay design so I think I will hold off on that for as long as possible.

Anyway, that's the bulk of what we've got so far! Still to do is pixel-pixel interactions, such as ensuring that sand displaces water, and adding support for upwards-moving elements such as gas. It may also be possible to implement water via an actual attempt at approximating fluid dynamics, but that doesn't seem so important for this initial prototyping phase.

News index
Choosing an Engine  ▸