It’s been over three years since I started working on Tribe Of Pok. Over that time I’ve grown as a developer and found better ways of doing things. One example is water flow mechanics. It’s one of the first things I worked on and has undergone several iterations.
When I was a newbie programmer with barely any experience, I was creating a new object for every unit of rain that fell and then destroying it when it evaporated or soaked into the ground. If you have any experience as a programmer, you’ll know how disastrously bad of an idea it is to create/destroy hundreds of objects every second. That implementation was quickly scrapped when my frame rate dropped to 10 FPS every time it rained.
Next, I tried storing the amount of water in each tile and performing water calculations only on the tiles. Performance was much better. All I had to do was +1 for every unit of rain that fell and -1 for every evaporation/saturation event. Simply calculate the height of each tile to determine if water can flow into it from an adjacent tile.
However when it rained, the ground would become blanketed in a layer of water. Hills would be just as saturated as valleys and there was nowhere for Pokians to hide. As a side effect, dry ponds would never completely refill since much of the water was stuck on higher ground. I fixed this by introducing movement to saturation. In effect, the water in the soil was “flowing” downhill just as the water above ground did.
Now water was flowing somewhat realistically and wasn’t hampering gameplay.
A year or so later I added rivers. Rivers form long, narrow paths that move in one direction. However with the simulation as it was, water moved in a random direction with each update. Sometimes water would flow into an adjacent hex. Then on the next update it would randomly decide to flow back into its starting hex. The result was water taking too long to go downriver. For particularly long rivers, the water evaporated before it even reached the other end of the map!
To fix this, I’m cheating by teleporting water from the high points to the low points along the river. Eventually I’ll replace teleporting water with a better method. In the meantime, I’m iterating and improving other aspects of the game.
I think what you are missing and is causing problems is lack of proper scale. Rainfall is usually measured in (few) millimeters per hour, and amounts to usually less than one meter for a whole month. (OK, In tropical climate the rain might pour down more quickly.) Terrain features, on the other hand are measured in at least several meters, if not dozens or hundreds. A river bed would be from one to several meters below the surrounding ground level, and drop a few meters for each kilometer along the path.
Another thing to consider is that water seepage to the ground of course depends on the soil type, but can be generally thought to be quite a slow process. Maybe the first few centimeters of rain is quickly absorbed as the topsoil gets wet, but after that it can take several days for even a small puddle to get absorbed and evaporate. Evaporation is of course highly dependant on relative humidity of the air and heat. My point is that it is maybe 1000 times or more quicker for water to flow around than to get absorbed or evaporate. This means that surface water will always flow down to accumulate into ponds and rivers. Only if it stays still, will it get absorbed to the ground or evaporate.
PS: For water flow between tiles, you will need to calculate the water surface height that is ground height + water height, and always move the water around so that the water surface heights are balanced between adjacent tiles. Greater the difference, higher the water flow. You should also balance the levels between all neighbour tiles, not just a random one. More realistic water simulation requires some realism from the height map as well, or you may find that it becomes a giant lake when the rain season begins.
Regarding the water seepage from soil to the river bank being slow, currently the seepage happens quite quickly and fills the river shortly after it’s rained. I’ll experiment with keeping some saturation in the soil and slowly releasing it to see if that helps fill up the river.
Regarding the height + water for water flow, I didn’t mention it but that’s what I’m currently using. It works well as you can see from the second gif (lighter tiles are higher ground), with water preferring to go downhill. I might be able to modify my heightmap so the rivers can gradually get lower as you mentioned. It would require a much finer measurement of each cell’s height though.
Thanks, you’ve given me some good things to think about.