Water 1

I had an idea for a game similar to Factorio, but entirely based around fluids and pipes (more detail on why I decided on this ridiculous idea to come). The underlying technology behind this would be a fluid movement system, which had to be really solid in order to build a game out of.

Thankfully, the Factorio devs have a very good devlog, and so I set to work reading everything they had written about fluids, as a jumping off point. As one might expect, it’s rather complicated, but at a high level, it boils down to a system that can compute inputs, outputs, and flow rates for a network of pipes and associated machinery. The concept is fairly simple, it’s not particularly difficult to design a game around, the bulk of the engineering task becomes efficiently simulating flow rates across a complex system.

I’ve also been playing Noita which has very responsive water (and oil, and ice, and wood, and fire, and… well, everything). They simulate fluids on an individual particle level, and it flows around in very pleasing ways. This has some advantages over a Factorio-esque simulation: It’s fun to splash around with even without a game around it, it looks nice automatically, and the granularity of simulation opens up some interesting game play systems. By this I mean that individual particle-level simulation can make more complex chemistry easy to develop, if two particles of different substances are next to each other, they might react. This opens up space for players to solve much more interesting problems. It’s also, in my opinion at the time, much more fun to build than pipes that do math.

Noita works on a falling sands model fluid simulation, which is painfully simple. For each water particle, if the cell below it is empty, move there. If not, look at the two diagonally down-left and down-right, and go to one of those if possible. If those are both full, check directly to the left and right, and move to one of those if possible. Boom, water. It flows, it puddles, it falls nicely.

Alas, there are several key problems with this. The most immediate: pressure. The “standard” falling sands model doesn’t take into account pressure, and so gives non-realistic situations really easily, for example:

This lack of pressure also breaks the fundamental factory fluid piece: pipes. A horizontal pipe full of water needs some pressure at one end to flow, and that means the water needs to know what pressure means.

Through a lot of casting about and trial and error, I discovered that this is really hard. There are quite a few options in designing such a system, and one of the big ones that I consciously decided was that fluids cannot be discrete particles on a grid, each grid cell would have to be able to contain variable amounts of fluid. Although discrete fluid units made some things easier, it was impossible to move small amounts of fluid around, which you would need for sensitive pressure systems. Instead each grid cell would have a “fluid amount,” which is a weird combination mass-density.
(It turns out that decision was most likely wrong, but we’ll get to that).

To simulate pressure, each cell would compute a desired fluid amount, by looking at all its neighbors, adjusting slightly for relative height, and getting their average. Then, if it had more fluid than it wanted, it would simply distribute the excess evenly among its neighbors. Gravity and walls worked along similar lines.

This worked shockingly well, and with some tweaking gave a nice looking approximation of a fluid. It flowed, it pressured, it even pushed water out of containers faster if they were under more pressure. It was simple, performant, and almost trivial to push to the GPU if necessary. Expanding out to multiple fluids, and mixing, and chemistry, should be easy.

Until I realized another key problem with this style of simulation: there was a speed limit. Although fluids could push any amount of fluid through an individual cell, and so could achieve any different flow rate, the edge of a body of fluid could only advance one cell at a time, it didn’t have any idea of velocity.

This would maybe have been okay, but it precluded a lot of interesting game play. Leaking high-pressure pipes couldn’t spurt across a factory, because they don’t know how fast they should be going when they left a pipe. Fluids can’t be injected violently into other fluids, only diffuse where they met.

popcorn.gif

I tried tacking velocity into my current solution, but everything quickly turned into popcorn. The discrete time steps with the discrete locations and volumes caused untold problems. It was very frustrating.

This prompted a return to the drawing board, and I discovered a whole world of existing fluid simulation models that I had somehow not found on my first pass. Some of them, I am proud to report, were quite similar to the solution that I had cooked up, and solved the problems that I had discovered in similar ways. I also discovered many other simulation models, which make different fundamental assumptions about how to represent the state of the world. Moving forward I am planning on trying a couple different models, and ready some of the vast ocean of papers that I have discovered on the topic.

Previous
Previous

Testing in Unity

Next
Next

Plants 2: Secondary Growth