Question

I'm working on a project using C#/XNA and I'm having trouble creating water-physics in an top-down hex-based grid.

We're using an hex-tilemap with help of: http://www.redblobgames.com/grids/hexagons. So I thought I could implement an algoritm for the water flow, but I can't seem to get it right, and it seems to be very performance-heavy.

/// <summary>
/// This method will level all the water to the same height. It does so by looking arround and make them all even
/// </summary>
/// <param name="tiles">The tile array needed to level arround</param>
public void Flow(List<Tile> tiles, Tilemap tileMap)
{
    float waterAmountEachTile;
    List<Water> waterTiles = new List<Water>(7);
    //include self
    waterTiles.Add(this);
    float waterAmount = (this.waterHeight + this.ZPos);

    for (int i = 0; i < tiles.Count; i++)//first loop to get all values
    {
        if (tiles[i].GetType() == typeof(Water))
        {
            waterTiles.Add((Water)tiles[i]);//check wich tiles are water and put them in a new array
            waterAmount += (waterTiles[waterTiles.Count - 1].waterHeight + waterTiles[waterTiles.Count - 1].ZPos);  //Increase the ammount - debuggen later werkt count goed
        }
    }

    waterAmountEachTile = waterAmount / waterTiles.Count; //Calculate how high each tile should be ( we need this for drycheck)
    dryCheck(ref waterAmount, waterTiles, waterAmountEachTile, tileMap);
    waterAmountEachTile = waterAmount / waterTiles.Count; //recalculate the ammount for each tile

    foreach (Water waterTile in waterTiles) //second loop to adjust the tile to the according hight
    {
        waterTile.waterHeight = (waterAmountEachTile - waterTile.ZPos);
    }
}

/// <summary>
/// Checks if the tile should be dry or continue being a water tile.
/// </summary>
/// <param name="waterAmount"> the ammount of water to divide among the tiles</param>
/// <param name="waterTiles">The watertiles list to do the drycheck on</param>
/// <param name="waterAmountEachTile">The height to set each water tile</param>
/// <returns></returns>
private void dryCheck(ref float waterAmount, List<Water> waterTiles, float waterAmountEachTile, Tilemap tileMap)
{
    //TODO dit fixen
    for (int i = 0; i < waterTiles.Count; i++)
    {
        if (waterTiles[i].ZPos > waterAmountEachTile) //is grond hoger dan water
        {
            waterAmount -= waterTiles[i].ZPos;
            tileMap.TileMap[waterTiles[i].XPos][waterTiles[i].YPos] = new Ground(this.graphics, waterTiles[i].XPos, waterTiles[i].YPos,
                waterTiles[i].ZPos, this.size, Tilemap.HexVertices);

            waterTiles.Remove(waterTiles[i]);
            i--;
        }
    }
}

Now for my question, does any of you know of a way to implement water-physics in a top-down environment, preferably with hex-based grids.

I've looked in to several liberaries, and found Smoothed-particle hydrodynamics, but I'm not sure if it's implementable top-down, and I can't seem to find any guides in that direction.

Any help would be great, even some pointers might be enough.

Thanks in advance, C. Venhuizen

Was it helpful?

Solution

Have you profiled your code to determine what is the slowest part?

I don't quite understand what your code is doing. Do you call Flow once for each tile, or do you call it once, and it runs over all tiles? If I were to guess, I'd say that allocating a new list each tile is going to be pretty slow. But the best way to know is to profile.

The thing that originally led me to write http://www.redblobgames.com/grids/hexagons was a top-down hex game that was all about water flow. I wrote that game in 1995, and I recently ported it to run on the web here. The algorithm I started with is simple. For each hex, traversed in random order:

  1. calculate the water level W + elevation H
  2. calculate the same for each neighbor
  3. make up to half the water flow to the neighbor with the lowest W + H

The random order is so that the loop doesn't cause water to flow many hexes to the right but not many hexes to the left, or other such direction artifacts. Even cleaner would be to use a double buffer for this: write all the new values to a separate array, and then copy them back to the first array at the end (or swap the arrays).

Beyond that, there are tons of heuristics I used for erosion and other features of the (unfinished) game. The code is old and awful but if you want to take a look, you can download it here (ZIP file). See water.cpp. I avoided memory allocations in the water flow loop.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top