我使用字节数组来描述游戏中的地形,每个字节值代表不同的 block 类型
我创建了一个网格来模拟水的传播,我为一个 block 上的水量提供一个字节值,然后通过增加一个 block 的值来传播它,这会导致它传播到具有水少了,我做了一个小视频来展示:(值55是一堵墙,0空气,1到4水,它是自上而下的视角)
https://www.youtube.com/watch?v=SOmnejfmPe0
但是我在完成它时遇到了困难,我不希望水位上升到 4 以上,当已经处于值 4 的 block 变为 5 时,我希望它流过所有其他 block ,直到所有包含的水都被值为 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;
}
这是我试图添加的条件,但它在所有可用 block 达到 4 之前就中断了,(水被困在两个 block 之间的无限循环中,这两个 block 无限地互相传递水)
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)
希望得到有关如何解决此问题的建议,谢谢!
最佳答案
这是您正在寻找的行为吗?
假设你有一个字节类型的2D网格,你可以使用DFS或BFS来实现这一点。 就我而言,我创建了一个具有 Value 属性的 Cell 类,设置该属性后,会自动更新单元格的文本和颜色。
接下来,我制作了一个网格管理器来存储单元格的 2D 网格。触发功能驻留在管理器中。每当我触发某个单元格时,它就会运行 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;
}
希望这有帮助!
关于c# - 基于字节数组的地形中的流水,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60960372/