我试图创建一个油漆桶风格的洪水填补瓷砖系统,以创建一个编辑器。
然而,基于四路泛色填充算法会产生问题。x + 1和y + 1工作得很好,但是当它们与x - 1和y - 1配对时,它会导致堆栈溢出。另外,我比较的点是平铺的颜色。有一些情况下,它会忽略颜色是否匹配,并在它应该退出时覆盖它们。
按照这里的示例算法,这段代码看起来应该可以工作:
Flood Fill Algorithm Example
然而,如上所述,我自己的C#实现并不是在所有方面都能正常工作:
public void FloodFill(int x, int y, Color fillColor, Color oldColor)
{
if (x < 0 || x >= boardSize) return;
if (y < 0 || y >= boardSize) return;
Tile tile = world.GetTileAt(x, y);
if (tile.currentColor.Equals(fillColor))
{
return;
}
if (tile.currentColor.Equals(oldColor))
{
UpdateTile(tile.currentTile, fillColor);
FloodFill(x - 1, y, fillColor, oldColor);
FloodFill(x + 1, y, fillColor, oldColor);
FloodFill(x, y - 1, fillColor, oldColor);
FloodFill(x, y + 1, fillColor, oldColor);
}
return;
}
我最好的猜测是,打个比方,它自己绊倒了,但这不应该发生在比较和返回以阻止执行的过程中,这个结论来自于日志--它在处理正数和负数时都会跳格。
以下是到目前为止的日志:
MasterController.FloodFill (System.Int32 x, System.Int32 y, UnityEngine.Color fillColor, UnityEngine.Color oldColor) (at Assets/Game Assets/Scripts/Gameplay/MasterController.cs:134)
MasterController.FloodFill (System.Int32 x, System.Int32 y, UnityEngine.Color fillColor, UnityEngine.Color oldColor) (at Assets/Game Assets/Scripts/Gameplay/MasterController.cs:136)
MasterController.FloodFill (System.Int32 x, System.Int32 y, UnityEngine.Color fillColor, UnityEngine.Color oldColor) (at Assets/Game Assets/Scripts/Gameplay/MasterController.cs:138)
MasterController.FloodFill (System.Int32 x, System.Int32 y, UnityEngine.Color fillColor, UnityEngine.Color oldColor) (at Assets/Game Assets/Scripts/Gameplay/MasterController.cs:136)
MasterController.FloodFill (System.Int32 x, System.Int32 y, UnityEngine.Color fillColor, UnityEngine.Color oldColor) (at Assets/Game Assets/Scripts/Gameplay/MasterController.cs:138)
MasterController.FloodFill (System.Int32 x, System.Int32 y, UnityEngine.Color fillColor, UnityEngine.Color oldColor) (at Assets/Game Assets/Scripts/Gameplay/MasterController.cs:136)
MasterController.FloodFill (System.Int32 x, System.Int32 y, UnityEngine.Color fillColor, UnityEngine.Color oldColor) (at Assets/Game Assets/Scripts/Gameplay/MasterController.cs:138)
以下是更新平铺和获取平铺位置的代码:
public void UpdateTile(Transform tile, Color color)
{
Mesh mesh = tile.GetComponent<MeshFilter>().mesh;
Color32[] nextColor = new Color32[mesh.vertices.Length];
for (int i = 0; i < nextColor.Length; i++)
{
nextColor[i] = color;
}
mesh.colors32 = nextColor;
}
public Tile GetTileAt(int x, int y)
{
if (x > size || x < 0 || y > size || y < 0)
{
return null;
}
return tiles[x, y];
}
3条答案
按热度按时间pbossiut1#
**大图像解决方案。**在我自己的一个项目中,我根据我发现的其他几个解决方案优化了一个解决方案。首先,这个解决方案通过使用一个带有Point堆栈的while循环避免了递归。其次,它通过检查堆栈中当前像素与目标像素的状态避免了自身重叠。如果当前像素是目标颜色,它只添加周围的项。这样,这个函数避免了再次查看一个像素,当指向的像素已经填充时快速退出。2这避免了使用不同的数据结构来存储你已经访问过的位置,正如Flater上面所建议的。3当试图填充大图像的大部分时,这个解决方案确实有它自己的弱点,下面将讨论。
其他优化我发现了其他直接使用位图的解决方案,在创建项目时,我通过在每个FloodFill开始时将System.Drawing.Image转换为位图来使用这些解决方案()调用,后来我发现这种转换消耗了大量内存,因为我正在处理的大型图像对象的BMP表示变得笨拙,相反,将System.Drawing.Image转换为位图并使用必要的GetPixel()和SetPixel()函数避免了大量的内存占用。2这使得编辑/泛洪大图像的部分非常友好。
弱点这个算法使用了一个Stack,当处理大图像时,它会很快变大。对于我的项目,限制泛色填充区域的大小是可以接受的,我已经在上面的代码中使用了overfill_counter。
vx6bjr1n2#
经过几个小时的尝试,我终于找到了为什么事情不能正常工作的答案。在进入洪水填充步骤之前,还有几个步骤需要确认。
(Note:activeColor是方法外部的公共变量)
然而,虽然这没有崩溃,但它并不完美。有一些情况下,它往往忽略下一个颜色,并填补了整个董事会与错误的颜色,所以我的比较仍然需要工作。
5f0d552i3#
问题是
你遇到了一个递归问题,看看下面的伪代码会有所帮助:
在我将要使用的示例中,让我们使用国际象棋棋盘作为参考:
假设我们从B4开始,这意味着该方法将对B4执行以下步骤:
那么
check A4
需要什么呢?这是这个方法的另一个示例,对于A4,它执行以下操作你最初的B4检查需要检查A4,随后的A4检查又会导致检查B4,这个新的B4检查会再次检查A4,那个A4检查会再次检查B4,如此无限重复。
解决方案
为了防止这个问题,你必须写你的逻辑,这样你就可以避免一遍又一遍地检查同一个单元格。
这可以通过几种方法来实现,但最简单的方法是跟踪您访问过的每个单元格,如果您对已经访问过的单元格运行检查,则立即从函数返回。
大致是这样的:
这可以确保您不会遇到两个邻居不断相互检查的问题。
我添加了
Cell
类,以避免在每个回合都要处理坐标变量-请务必检查您的环境中是否还没有现有的类/结构来表示2D点。一个好建议
如果您记录了正在检查的单元格,您可能会遇到这个问题。
你会发现它会一遍又一遍地重复检查相同的两个单元格。