Henry Smith

🚀 make things
📝 write about it

Simulating Water

I’ve been having fun implementing falling sand physics recently. So I wanted to try and share a sense of what it’s like to work on. Just making 2D water fall and flow convincingly is quite tricky, so let’s use that as an example.

If you’re not familiar with falling sand games, then check out pok5.de’s Element Dots for a fairly complete JavaScript implementation. The gist of it is that it’s a 2D grid of particles. You simulate and repaint the grid as many times per second as you can. If you do a good job of the simulation logic, you end up with a convincing 2D physics sandbox that people will play with for hours.

In my early-stage implementation, there are three types of elements: air, stone and water. Each particle in the grid is one of those three elements. By default, the grid’s full of white air particles. In the middle of that, I hardcoded a little U-shaped “cup” of grey stone particles. After that, I generated a “droplet” of about 100 blue water particles hovering just above the cup. This is what all that looks like.

simulating-water/still.png

On each tick of the simulation, each particle in the grid invokes the behavioural rules of its element. The trick is making the behaviour efficient and convincing. It has to be fast because the simulation is invoking it so many times per second. And it has to somehow be clever enough to make the whole experience come together and feel like more than the sum of its parts. Balancing those two constraints against each other is a big part of the challenge of making a falling sand game.

First, we want to make that water fall into the cup. That means making water particles fall downwards through air. The most simplistic way of implementing this is to go through all the water particles, look at the particle below each one, and swap places with it if it’s an air particle. And this turns out to be enough to get the droplet to fall to the bottom of the cup.

The droplet falls, but it’s all a bit rubbish, isn’t it? After hitting the bottom of the cup, it sits bolt upright like an ice cube. Its blue colour is the only clue that it’s even supposed to be water at all. Water particle movement is more complex than downwards motion through air. Water flows, deforms, and fills the shape of its container.

For the next iteration, I added some extra capabilities to water. On top of its existing ability to move straight down, I added diagonal downwards movement. So when a water particle can’t move down, it can “flow” past the obstacle below by appearing to “roll” off to one side. This definitely fixed the “ice cube” effect.

It’s better, but it’s still not quite water though. It deforms and spreads out on impact, but it still doesn’t flow like water. This motion looks more like a “splat” than anything. It’s like a weird blue shit landing in a cup, but it’s a definite improvement.

To turn the blue shit into water, I added another couple of behavioural rules. At this stage, water was able to move in three downward directions. To these three downward motions, I added horizontal movement. So when a water particle can’t move downwards in any direction, it falls back to “flowing” left and right.

That’s better! There’s still a bit of weirdness to this movement which needs ironing out eventually, but it’s recognisable as water now. It hits the bottom of the cup, deforms, spreads out, and fills all the available space. Some of that “wave” behaviour during the deformation is a bit unrealistic, but it’s close enough for now. It’s certainly not bad for ten lines of water behaviour code.

behave: function(particle) {
  if (particle.isNorthOf(Sands.Element.Air)) {
    particle.moveSouth();
  } else if (particle.isNorthWestOf(Sands.Element.Air)) {
    particle.moveSouthEast();
  } else if (particle.isNorthEastOf(Sands.Element.Air)) {
    particle.moveSouthWest();
  } else if (particle.isEastOf(Sands.Element.Air)) {
    particle.moveWest();
  } else if (particle.isWestOf(Sands.Element.Air)) {
    particle.moveEast();
  }
}

I hope I’ve convinced you that falling sand particle simulation is a fun type of thing to build. And it’s accessible. The world representation is just a 2D array. And if you can figure out how to draw coloured squares in your platform of choice, you’re a good enough graphics programmer for the job, too. Totally worth a look.