2D Tile based water using cellular automata.

Started by
0 comments, last by YasinKhalil 11 years, 5 months ago
I've been trying to implement tile based water similar to terraria. I'm using C++ and SDL.
Here is my code for the simulation aspect of the water tiles.
My code is based off this java tutorial (http://w-shadow.com/...uid-simulation/)

void App::SimulateCompression()
{
float Flow = 0;
float remainingmass = 0;
//Calculate and apply flow for each block
for (int X = 0; X < MAP_WIDTH; X++)
{
for(int Y = 0; Y < MAP_HEIGHT; Y++)
{
int ID = Rect2Lin(TILE_SIZE,X,Y);
int Up = Rect2Lin(TILE_SIZE,X,(Y-1));
int Down = Rect2Lin(TILE_SIZE,X,(Y+1));
int Left = Rect2Lin(TILE_SIZE,(X-1),Y);
int Right = Rect2Lin(TILE_SIZE,(X+1),Y);
//Skip inert ground blocks
if(TileList[ID].TileProp == TILE_GROUND) continue;
//Custom push-only flow
Flow = 0;
remainingmass = TileList[ID].Mass;
if(remainingmass <= 0)continue;
//The block below this one
if(TileList[Down].TileProp != TILE_GROUND)
{
Flow = GetStableWaterState(remainingmass + TileList[Down].Mass) - TileList[Down].Mass;
if(Flow > MinFlow){Flow *= 0.05f;}
Flow = Constrain(Flow, 0, Min(MaxSpeed, remainingmass));
TileList[ID].NewMass -= Flow;
TileList[Down].NewMass += Flow;
remainingmass -= Flow;
}
if(remainingmass <= 0)continue;
//Left
if(TileList

.TileProp != TILE_GROUND)
{
//Equalize the amount of water in this block and it's neighbour
Flow = (TileList[ID].Mass - TileList

.Mass)/4;
if(Flow > MinFlow){Flow *= 0.05f;}
Flow = Constrain(Flow, 0, remainingmass);
TileList[ID].NewMass -= Flow;
TileList

.NewMass += Flow;
remainingmass -= Flow;
}
if(remainingmass <= 0)continue;
//Right
if(TileList

.TileProp != TILE_GROUND)
{
//Equalize the amount of water in this block and it's neighbour
Flow =(TileList[ID].Mass - TileList

.Mass)/4;
if(Flow > MinFlow){Flow *= 0.05f;}
Flow = Constrain(Flow, 0, remainingmass);
TileList[ID].NewMass -= Flow;
TileList

.NewMass += Flow;
remainingmass -= Flow;
}
if(remainingmass <= 0)continue;
//Up. Only compressed water flows upwards.
if(TileList[Up].TileProp != TILE_GROUND)
{
Flow = remainingmass - GetStableWaterState(remainingmass + TileList[Up].Mass);
if (Flow > MinFlow){Flow *= 0.05f;}
Flow = Constrain(Flow, 0, Min(MaxSpeed, remainingmass));
TileList[ID].NewMass -= Flow;
TileList[Up].NewMass += Flow;
remainingmass -= Flow;
}
}
}

//Copy the new mass values
for (int X = 0; X < MAP_WIDTH; X++)
{
for (int Y = 0; Y < MAP_HEIGHT; Y++)
{
int ID = Rect2Lin(TILE_SIZE,X,Y);
TileList[ID].Mass = TileList[ID].NewMass;
}
}
for(int X = 0; X < MAP_WIDTH; X++)
{
for(int Y = 0; Y < MAP_HEIGHT; Y++)
{
int ID = Rect2Lin(TILE_SIZE,X,Y);
//Skip ground blocks
if(TileList[ID].TileProp == TILE_GROUND) continue;
//Flag/unflag water blocks
if(TileList[ID].Mass > MinMass)
{
TileList[ID].TileProp = TILE_WATER;
}else
{
TileList[ID].TileProp = TILE_AIR;
}
}
}
//Remove any water that has left the map
for(int X = 0; X < MAP_WIDTH; X++)
{
TileList[Rect2Lin(TILE_SIZE,X,0)].Mass = 0;
TileList[Rect2Lin(TILE_SIZE,X,0)].TileProp = 0;
TileList[Rect2Lin(TILE_SIZE,X,MAP_HEIGHT - 1)].Mass = 0;
TileList[Rect2Lin(TILE_SIZE,X,MAP_HEIGHT - 1)].TileProp = 0;
//mass[x][0] = 0;
//mass[x][map_height+1] = 0;
}
for(int Y = 0; Y < MAP_HEIGHT; Y++)
{
TileList[Rect2Lin(TILE_SIZE,0,Y)].Mass = 0;
TileList[Rect2Lin(TILE_SIZE,0,Y)].TileProp = 0;
TileList[Rect2Lin(TILE_SIZE,(MAP_WIDTH - 1),Y)].Mass = 0;
TileList[Rect2Lin(TILE_SIZE,(MAP_WIDTH - 1),Y)].TileProp = 0;
//mass[0][y] = 0;
//mass[map_width+1][y] = 0;
}
}



The embeded video shows what happens when using this function. I really need help understanding why I'm not getting the same effects as the tutorial. Maybe, guide me on what I could do to fix these issues. I discovered that it has to do with the "flow" and how the masses are not being distributed among the other tiles. Any Ideas?




[Media]
[/Media]
Advertisement
I fixed the issues myself(took me a while) here is the final video of it working.
[Media]
[/Media]

This topic is closed to new replies.

Advertisement