基于字节数组的地形中的流水
Flowing Water in Byte Array based Terrain
我在游戏中使用字节数组来描述地形,每个字节值代表不同的方块类型
我创建了一个网格来模拟水的传播,我给一个块上的水量一个字节值,然后我通过增加一个块的值来传播它,这导致它传播到块水较少,我制作了一个小视频来展示:(值 55 是一堵墙,0 空气,1 到 4 水,它是自上而下的视角)
https://www.youtube.com/watch?v=SOmnejfmPe0
但是我在完成它时遇到了麻烦,我不希望水位上升到 4 以上,当一个已经处于值 4 的块变为 5 我希望它流过所有其他块,直到所有包含水的值为 4。但是,对于我当前的代码,如果我设置条件以使水流动,无论它的值为 4 或更大,它都会以无限循环结束。
这里是当前的相关代码:
public Vector3Int PosToCheck;
public IEnumerator FlowRoutine(int x, int y, int z)
{
PosToCheck = KeepPosInBounds(new Vector3Int(x - 1, y, z));
if ( GridArray[x,y,z] > GridArray[PosToCheck.x,PosToCheck.y,PosToCheck.z] + 1 && GridArray[PosToCheck.x, PosToCheck.y, PosToCheck.z] != 55)
{
GridArray[x, y, z] -= 1;
GridArray[PosToCheck.x, PosToCheck.y, PosToCheck.z] += 1;
yield return StartCoroutine(FlowRoutine(PosToCheck.x, PosToCheck.y, PosToCheck.z));
}
else
{
PosToCheck = KeepPosInBounds(new Vector3Int(x, y + 1, z));
if (GridArray[x,y,z] > GridArray[PosToCheck.x,PosToCheck.y,PosToCheck.z] + 1 && GridArray[PosToCheck.x, PosToCheck.y, PosToCheck.z] != 55)
{
GridArray[x, y, z] -= 1;
GridArray[PosToCheck.x, PosToCheck.y, PosToCheck.z] += 1;
yield return StartCoroutine(FlowRoutine(PosToCheck.x, PosToCheck.y, PosToCheck.z));
}
else
{
PosToCheck = KeepPosInBounds(new Vector3Int(x + 1, y, z));
if (GridArray[x,y,z] > GridArray[PosToCheck.x,PosToCheck.y,PosToCheck.z] + 1 && GridArray[PosToCheck.x, PosToCheck.y, PosToCheck.z] != 55)
{
GridArray[x, y, z] -= 1;
GridArray[PosToCheck.x, PosToCheck.y, PosToCheck.z] += 1;
yield return StartCoroutine(FlowRoutine(PosToCheck.x, PosToCheck.y, PosToCheck.z));
}
else
{
PosToCheck = KeepPosInBounds(new Vector3Int(x , y-1, z));
if (GridArray[x,y,z] > GridArray[PosToCheck.x,PosToCheck.y,PosToCheck.z] + 1 && GridArray[PosToCheck.x, PosToCheck.y, PosToCheck.z] != 55)
{
GridArray[x, y, z] -= 1;
GridArray[PosToCheck.x, PosToCheck.y, PosToCheck.z] += 1;
yield return StartCoroutine(FlowRoutine(PosToCheck.x, PosToCheck.y, PosToCheck.z));
}
}
}
}
UpdateAllButtons();
yield return null;
}
public Vector3Int KeepPosInBounds(Vector3Int newPos)
{
if (newPos.x < 0) { newPos.x = XGridSize - 1; }
if (newPos.y < 0) { newPos.y = YGridSize - 1; }
if (newPos.z < 0) { newPos.z = ZGridSize - 1; }
if (newPos.x >= XGridSize) { newPos.x = 0; }
if (newPos.y >= YGridSize) { newPos.y = 0; }
if (newPos.z >= ZGridSize) { newPos.z = 0; }
return newPos;
}
这是我试图添加的条件,但它在所有可用块都为 4 之前中断,(水被困在 2 个块之间的无限循环中,无限地相互传递水)
if ((GridArray[x,y,z] > GridArray[PosToCheck.x,PosToCheck.y,PosToCheck.z] + 1 || GridArray[x, y, z]>=WaterCap) && GridArray[PosToCheck.x, PosToCheck.y, PosToCheck.z] != 55)
希望得到有关如何解决此问题的建议,谢谢!
这是您正在寻找的行为吗?
假设你有一个字节类型的二维网格,你可以使用DFS或者BFS来实现。
在我的例子中,我制作了一个单元格 class,其值 属性 设置后,会自动更新单元格的文本和颜色。
接下来,我制作了一个网格管理器,用于存储单元格的二维网格。触发器函数驻留在管理器中。每当我触发某个单元格时,它都会运行 DFS 以从该单元格开始进行更新。 DFS逻辑有条件检查边界和墙,也限制了水的溢出。
void Trigger(int x, int y)
{
bool[,] visited = new bool[yCount, xCount];
for(int i=0; i<yCount; i++)
{
for(int j=0; j<xCount; j++)
{
visited[i, j] = false;
}
}
DfsUpdate(y, x, visited);
}
void DfsUpdate(int i, int j, bool[,] visited)
{
// Check out of bounds
if (i < 0 || i >= yCount) return;
if (j < 0 || j >= xCount) return;
if (visited[i, j]) return;
// Mark as visited
visited[i, j] = true;
if (cells[i, j].Value == 55) // Its a wall
return;
if (cells[i, j].Value == 0)
{
// Needs 1 update and return
cells[i, j].Value++;
return;
}
// Restrict upto 4 if its water
cells[i, j].Value = (byte) Mathf.Min(cells[i, j].Value + 1, 4);
// Recursively call neighbouting cells
DfsUpdate(i + 1, j, visited);
DfsUpdate(i - 1, j, visited);
DfsUpdate(i, j + 1, visited);
DfsUpdate(i, j - 1, visited);
}
您也可以使用 BFS,但 DFS 在这里工作得很好,并且在 DFS 的情况下实现起来更容易。
编辑:
现在需要72次点击才能填满网格。检查左下角以查看鼠标点击次数。
我更改了密码。现在,当我单击一个单元格时,如果它已经是 4(已填充),它会找到最近的未完全填充的网格并向其添加 1,否则它会自行填充。
在 Update 方法中,我以特定速率遍历网格并检查是否有任何单元格具有相邻的单元格,使得它们的水位差为 2。我填充该单元格并将其标记为已更新,这样它们就不会在当前帧中再次更新。
网格更新方法是一个简单的 BFS,它向最近的未填充邻居(如果存在)加 1。
在更新方法中,使用计时器,每当超过某个延迟时,我都会遍历网格以平衡水位差异。
这是代码。
void GridUpdate(int i, int j)
{
bool[,] visited = new bool[yCount, xCount];
Queue<int> iQ = new Queue<int>();
Queue<int> jQ = new Queue<int>();
iQ.Enqueue(i);
jQ.Enqueue(j);
while(iQ.Count > 0)
{
int si = iQ.Dequeue();
int sj = jQ.Dequeue();
if (!CheckBounds(si, sj)) continue;
if (cells[si, sj].Value == 55) continue;
if (cells[si, sj].Value < 4)
{
cells[si, sj].Value++;
return;
}
// Up
iQ.Enqueue(si - 1);
jQ.Enqueue(sj);
// Down
iQ.Enqueue(si + 1);
jQ.Enqueue(sj);
// Left
iQ.Enqueue(si);
jQ.Enqueue(sj - 1);
// Right
iQ.Enqueue(si);
jQ.Enqueue(sj + 1);
}
}
void Update()
{
timer += Time.deltaTime * 1000f;
if (timer >= updateDelay)
{
timer -= updateDelay;
bool[,] updated = new bool[yCount, xCount];
for (int i = 0; i < yCount; i++)
{
for (int j = 0; j < xCount; j++)
{
if (updated[i, j] || cells[i,j].Value == 55) continue;
if (CheckBounds(i-1, j) && !updated[i-1, j] && cells[i, j].Value - cells[i - 1, j].Value >= 2)
{
updated[i - 1, j] = true;
cells[i - 1, j].Value++;
cells[i, j].Value--;
}
else if (CheckBounds(i+1, j) && !updated[i + 1, j] && cells[i, j].Value - cells[i + 1, j].Value >= 2)
{
updated[i + 1, j] = true;
cells[i + 1, j].Value++;
cells[i, j].Value--;
}
else if (CheckBounds(i, j-1) && !updated[i, j-1] && cells[i, j].Value - cells[i, j-1].Value >= 2)
{
updated[i, j-1] = true;
cells[i, j-1].Value++;
cells[i, j].Value--;
}
else if (CheckBounds(i, j+1) && !updated[i, j + 1] && cells[i, j].Value - cells[i, j + 1].Value >= 2)
{
updated[i, j + 1] = true;
cells[i, j + 1].Value++;
cells[i, j].Value--;
}
}
}
}
}
// Checks if a given i and j are not out of bounds of the grid
bool CheckBounds(int i, int j)
{
if (j < 0 || j >= xCount) return false;
if (i < 0 || i >= yCount) return false;
return true;
}
希望对您有所帮助!
我在游戏中使用字节数组来描述地形,每个字节值代表不同的方块类型
我创建了一个网格来模拟水的传播,我给一个块上的水量一个字节值,然后我通过增加一个块的值来传播它,这导致它传播到块水较少,我制作了一个小视频来展示:(值 55 是一堵墙,0 空气,1 到 4 水,它是自上而下的视角)
https://www.youtube.com/watch?v=SOmnejfmPe0
但是我在完成它时遇到了麻烦,我不希望水位上升到 4 以上,当一个已经处于值 4 的块变为 5 我希望它流过所有其他块,直到所有包含水的值为 4。但是,对于我当前的代码,如果我设置条件以使水流动,无论它的值为 4 或更大,它都会以无限循环结束。
这里是当前的相关代码:
public Vector3Int PosToCheck;
public IEnumerator FlowRoutine(int x, int y, int z)
{
PosToCheck = KeepPosInBounds(new Vector3Int(x - 1, y, z));
if ( GridArray[x,y,z] > GridArray[PosToCheck.x,PosToCheck.y,PosToCheck.z] + 1 && GridArray[PosToCheck.x, PosToCheck.y, PosToCheck.z] != 55)
{
GridArray[x, y, z] -= 1;
GridArray[PosToCheck.x, PosToCheck.y, PosToCheck.z] += 1;
yield return StartCoroutine(FlowRoutine(PosToCheck.x, PosToCheck.y, PosToCheck.z));
}
else
{
PosToCheck = KeepPosInBounds(new Vector3Int(x, y + 1, z));
if (GridArray[x,y,z] > GridArray[PosToCheck.x,PosToCheck.y,PosToCheck.z] + 1 && GridArray[PosToCheck.x, PosToCheck.y, PosToCheck.z] != 55)
{
GridArray[x, y, z] -= 1;
GridArray[PosToCheck.x, PosToCheck.y, PosToCheck.z] += 1;
yield return StartCoroutine(FlowRoutine(PosToCheck.x, PosToCheck.y, PosToCheck.z));
}
else
{
PosToCheck = KeepPosInBounds(new Vector3Int(x + 1, y, z));
if (GridArray[x,y,z] > GridArray[PosToCheck.x,PosToCheck.y,PosToCheck.z] + 1 && GridArray[PosToCheck.x, PosToCheck.y, PosToCheck.z] != 55)
{
GridArray[x, y, z] -= 1;
GridArray[PosToCheck.x, PosToCheck.y, PosToCheck.z] += 1;
yield return StartCoroutine(FlowRoutine(PosToCheck.x, PosToCheck.y, PosToCheck.z));
}
else
{
PosToCheck = KeepPosInBounds(new Vector3Int(x , y-1, z));
if (GridArray[x,y,z] > GridArray[PosToCheck.x,PosToCheck.y,PosToCheck.z] + 1 && GridArray[PosToCheck.x, PosToCheck.y, PosToCheck.z] != 55)
{
GridArray[x, y, z] -= 1;
GridArray[PosToCheck.x, PosToCheck.y, PosToCheck.z] += 1;
yield return StartCoroutine(FlowRoutine(PosToCheck.x, PosToCheck.y, PosToCheck.z));
}
}
}
}
UpdateAllButtons();
yield return null;
}
public Vector3Int KeepPosInBounds(Vector3Int newPos)
{
if (newPos.x < 0) { newPos.x = XGridSize - 1; }
if (newPos.y < 0) { newPos.y = YGridSize - 1; }
if (newPos.z < 0) { newPos.z = ZGridSize - 1; }
if (newPos.x >= XGridSize) { newPos.x = 0; }
if (newPos.y >= YGridSize) { newPos.y = 0; }
if (newPos.z >= ZGridSize) { newPos.z = 0; }
return newPos;
}
这是我试图添加的条件,但它在所有可用块都为 4 之前中断,(水被困在 2 个块之间的无限循环中,无限地相互传递水)
if ((GridArray[x,y,z] > GridArray[PosToCheck.x,PosToCheck.y,PosToCheck.z] + 1 || GridArray[x, y, z]>=WaterCap) && GridArray[PosToCheck.x, PosToCheck.y, PosToCheck.z] != 55)
希望得到有关如何解决此问题的建议,谢谢!
这是您正在寻找的行为吗?
假设你有一个字节类型的二维网格,你可以使用DFS或者BFS来实现。 在我的例子中,我制作了一个单元格 class,其值 属性 设置后,会自动更新单元格的文本和颜色。
接下来,我制作了一个网格管理器,用于存储单元格的二维网格。触发器函数驻留在管理器中。每当我触发某个单元格时,它都会运行 DFS 以从该单元格开始进行更新。 DFS逻辑有条件检查边界和墙,也限制了水的溢出。
void Trigger(int x, int y)
{
bool[,] visited = new bool[yCount, xCount];
for(int i=0; i<yCount; i++)
{
for(int j=0; j<xCount; j++)
{
visited[i, j] = false;
}
}
DfsUpdate(y, x, visited);
}
void DfsUpdate(int i, int j, bool[,] visited)
{
// Check out of bounds
if (i < 0 || i >= yCount) return;
if (j < 0 || j >= xCount) return;
if (visited[i, j]) return;
// Mark as visited
visited[i, j] = true;
if (cells[i, j].Value == 55) // Its a wall
return;
if (cells[i, j].Value == 0)
{
// Needs 1 update and return
cells[i, j].Value++;
return;
}
// Restrict upto 4 if its water
cells[i, j].Value = (byte) Mathf.Min(cells[i, j].Value + 1, 4);
// Recursively call neighbouting cells
DfsUpdate(i + 1, j, visited);
DfsUpdate(i - 1, j, visited);
DfsUpdate(i, j + 1, visited);
DfsUpdate(i, j - 1, visited);
}
您也可以使用 BFS,但 DFS 在这里工作得很好,并且在 DFS 的情况下实现起来更容易。
编辑:
现在需要72次点击才能填满网格。检查左下角以查看鼠标点击次数。
我更改了密码。现在,当我单击一个单元格时,如果它已经是 4(已填充),它会找到最近的未完全填充的网格并向其添加 1,否则它会自行填充。 在 Update 方法中,我以特定速率遍历网格并检查是否有任何单元格具有相邻的单元格,使得它们的水位差为 2。我填充该单元格并将其标记为已更新,这样它们就不会在当前帧中再次更新。
网格更新方法是一个简单的 BFS,它向最近的未填充邻居(如果存在)加 1。
在更新方法中,使用计时器,每当超过某个延迟时,我都会遍历网格以平衡水位差异。
这是代码。
void GridUpdate(int i, int j)
{
bool[,] visited = new bool[yCount, xCount];
Queue<int> iQ = new Queue<int>();
Queue<int> jQ = new Queue<int>();
iQ.Enqueue(i);
jQ.Enqueue(j);
while(iQ.Count > 0)
{
int si = iQ.Dequeue();
int sj = jQ.Dequeue();
if (!CheckBounds(si, sj)) continue;
if (cells[si, sj].Value == 55) continue;
if (cells[si, sj].Value < 4)
{
cells[si, sj].Value++;
return;
}
// Up
iQ.Enqueue(si - 1);
jQ.Enqueue(sj);
// Down
iQ.Enqueue(si + 1);
jQ.Enqueue(sj);
// Left
iQ.Enqueue(si);
jQ.Enqueue(sj - 1);
// Right
iQ.Enqueue(si);
jQ.Enqueue(sj + 1);
}
}
void Update()
{
timer += Time.deltaTime * 1000f;
if (timer >= updateDelay)
{
timer -= updateDelay;
bool[,] updated = new bool[yCount, xCount];
for (int i = 0; i < yCount; i++)
{
for (int j = 0; j < xCount; j++)
{
if (updated[i, j] || cells[i,j].Value == 55) continue;
if (CheckBounds(i-1, j) && !updated[i-1, j] && cells[i, j].Value - cells[i - 1, j].Value >= 2)
{
updated[i - 1, j] = true;
cells[i - 1, j].Value++;
cells[i, j].Value--;
}
else if (CheckBounds(i+1, j) && !updated[i + 1, j] && cells[i, j].Value - cells[i + 1, j].Value >= 2)
{
updated[i + 1, j] = true;
cells[i + 1, j].Value++;
cells[i, j].Value--;
}
else if (CheckBounds(i, j-1) && !updated[i, j-1] && cells[i, j].Value - cells[i, j-1].Value >= 2)
{
updated[i, j-1] = true;
cells[i, j-1].Value++;
cells[i, j].Value--;
}
else if (CheckBounds(i, j+1) && !updated[i, j + 1] && cells[i, j].Value - cells[i, j + 1].Value >= 2)
{
updated[i, j + 1] = true;
cells[i, j + 1].Value++;
cells[i, j].Value--;
}
}
}
}
}
// Checks if a given i and j are not out of bounds of the grid
bool CheckBounds(int i, int j)
{
if (j < 0 || j >= xCount) return false;
if (i < 0 || i >= yCount) return false;
return true;
}
希望对您有所帮助!