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

Slow Rush Studios

◂  Barrels of Boom
News index
Gesticulating Wildly  ▸

Flinging Powder Atoms

Contents

Being new to game dev while also working solo is great fun.

Most of the code was written by your least-favourite colleague: you from several months ago.

And the bigger your game gets, the more prolific that incompetent colleague seems to be.

But hey, you can always rewrite code, so that's what I did with the atom movement logic for powders (sand, coal, etc) this week.

Atom Movement Quirks

Atom movement has been "okay-ish" for a while, but as I've been adding more gameplay, the cracks were starting to show...

Before: A block of sand falling slows down when a corner hits a ledge

And you can't shoot atoms in the same direction, which limits what spells can do:

Before: trying (and failing) to shoot sand and water. Red line indicates shoot direction.

And on top of that:

This same "powder movement" code is at the heart of fire, smoke, and explosion-visuals too, so I figured it was time to fix it.

The Big Mistake

When I added "velocity" to atoms half a year ago, I made a terrible mistake.

You see, atoms are constrained to a 2D "integer grid". You can have an atom at 0, 0 or 0, 1 but not 0, 0.5.

But when I added velocity, I figured I needed a way for atoms to have a fractional position, because otherwise the velocity calculation would be physically incorrect: an atom would hang stationary in the air until their velocity had increased to be at least 1.0, which could take up to half a second. 1

So I added a special offset attribute to the atom that stored X and Y coordinates from 0.0 to 1.0, and introduced all kinds of special-case handling to account for that.

Turns out, it's totally unnecessary! Who cares about physically accurate! We've already increased gravity to 2X! 2

Removing that offset attribute allowed me to drastically simplify the entire atom movement logic.

1

In fairness, The Powder Toy also has a fractional position for atoms, so I had some reason to believe that it would work okay. But The Powder Toy allows atoms to overlap each other, which makes that approach far easier to reason about (at the cost of not being able to parallelize the update process easily).

2

In fact the higher gravity is already enough to help reduce this problem a fair bit.

Collision Hacks

Correctly working out how an atom should bounce when hitting another atom is tricky.

In theory, if two moving atoms hit each other in mid air, you can apply standard billiard-ball-like physics to them.

In practice, if you have a block of 4 or more atoms moving in the same direction, the "behind" atoms will end up boosting the "in front" atoms, because the atoms are only updated one at a time.

So I added a hack: an atom that moves in the same-ish direction another atom is not allowed to bounce; instead it just moves up next to that atom.

Atom Surfaces

Bouncing gets even more complicated: when an atom hits a line of atoms at a non-90 degree angle, you expect it to bounce realistically.

But if you treat the two colliding atoms as billiard balls in this case, the incoming atom will have its velocity totally reversed, which looks broken.

This is because the collision isn't happening between an atom and a line of atoms, but between two supposedly-spherical atoms. 3

3

In physics-programmer terms, the correct approach is to calculate the surface direction (the direction of the "line" of atoms.. bearing in mind the line could be crooked or an L-shape). Then you calculate the vector perpendicular to your surface direction; this is the collision normal. Then reflect the velocity about the collision normal.

What I ended up doing is looking at the atoms immediately around the atom that was hit, and using them to calculate the correct bounce.

Here's a slow-mo demo with an unusually-bouncy purple atom:

Collision in slow motion. Thin red line = moving atom velocity, purple circle = atom that was hit, orange/green circles = atoms either side (the detected 'line of atoms' hit), white line = calculated 'collision normal'.

Frustratingly, after implementing this (and friction too), I realized that powders look bad if they bounce too much, so you mostly don't see atoms bouncing.

But I ended up using the underlying calculations for other bits of movement too, so I think it's still a net win.

Results

I'll skip over the many other minor fixes and just show some results (compare them with above):

After: You can now fling sand in a direction and it mostly goes in that direction! (Water still needs work.)

Sand looks better when it hits a ledge:

After: sand block slows down less and slides more smoothly. Not perfect, but better.
(The atoms piling up on the right towards the end is due to a chunk boundary, unrelated to these changes.)

And on top of atom movement being more robust, the whole game now performs about 20% better across the board. 4

4

That's across the whole game, not just movement. And includes some optimizations to explosions which the new atom movement code allowed.

Playable web build‎

For your convenience, this week's build starts off in Edit-mode (F4 to toggle), so you can fire atoms just by left-clicking:

Aside: as a rule I don't put any effort in for mobile, but this week tapping the screen to fire atoms should actually work too!

Click to focus, then play with keyboard and mouse. Mobile devices may or may not work! Give feedback.

I did have to re-tweak a great deal of "tuned" (hardcoded) numbers to work with the new atom movement code, so let me know if anything seems off!


◂  Barrels of Boom
News index
Gesticulating Wildly  ▸