话不多说,先上效果:
白底表示格子(地板),蓝线表示包围框(墙)。
1、最简单的一种:
2、中间漏缝:
3、复杂一点的:
原理讲解
1、定义一个所有需要检测的格子区域,待检测区域,当前检测区域
2、如果待检测区域为空,将待检测区域中随机一个点放入待检测区域中。如果待检测区域也为空,说明已经遍历完了
3、取出待检测区域中第一个点。向前后左右四个方向检测
a、将取出的点从需要检测的格子区域中移除,从待检测区域中移除。
b、如果该方向有墙,不处理。
c、如果该方向没有墙,但是该方向下一个点不在需要检测的格子里,也不在当前检测区域中,说明已经超出边界。此时当前检测区域列表中的点,和当前待检测区域中的点,都不在封闭区域内。返回步骤2
d、将该方向的下一个点加入待检测列表
4、重复步骤3,当待检测区域为空时,当前检测区域内的点,就是封闭区域内的点。此时重复步骤2
代码如下,之所以字典里存了Transform,是测试时改变颜色的,各位可以自行修改存储结构
我是将格子坐标*2,然后墙坐标就可以放在两个格子中间
比如,格子(0,0) 和格子(2,0)是挨着的,中间墙的坐标为(1,0)。
//事先存好的所有点(坐标并非相邻,放大了两倍,方便取墙的坐标)
//比如,格子(0,0) 和格子(2,0)是挨着的,中间墙的坐标为(1,0)
Dictionary<int, Dictionary<int,Transform>> foundationMap = new Dictionary<int, Dictionary<int, Transform>>();
//事先存好的所有墙
Dictionary<int, Dictionary<int, Transform>> wallMap = new Dictionary<int, Dictionary<int, Transform>>();
public void Calculate()
{
Dictionary<int, List<int>> list = new Dictionary<int, List<int>>();
Dictionary<int, List<int>> closedMap = new Dictionary<int, List<int>>();
foreach (var item in foundationMap)
{
foreach (var temp in item.Value.Keys)
{
if (!list.ContainsKey(item.Key))
list.Add(item.Key, new List<int>());
list[item.Key].Add(temp);
}
}
int x = 0, z = 0;
foreach (var item in foundationMap)
{
foreach (var temp in item.Value.Keys)
{
x = item.Key;
z = temp;
break;
}
break;
}
if (foundationMap.Count > 0)
{
while (list.Count > 0)
{
Dictionary<int, List<int>> areas = new Dictionary<int, List<int>>();
Dictionary<int, List<int>> waitChecks = new Dictionary<int, List<int>>
{
{ x, new List<int>() }
};
waitChecks[x].Add(z);
bool outOfRange = false;
while (waitChecks.Count > 0)
{
int _x = 0, _z = 0;
foreach (var item in waitChecks)
{
_x = item.Key;
_z = item.Value[0];
break;
}
waitChecks[_x].Remove(_z);
if (waitChecks[_x].Count == 0)
waitChecks.Remove(_x);
list[_x].Remove(_z);
if (list[_x].Count == 0)
list.Remove(_x);
if (!areas.TryGetValue(_x, out List<int> m1) || !m1.Contains(_z))
{
if (!areas.ContainsKey(_x))
areas.Add(_x, new List<int>());
areas[_x].Add(_z);
//这个是检测周围4个方向的点的坐标
int[,] tb = new int[4, 2]
{
{ _x,_z + 2},
{_x + 2,_z },
{_x,_z - 2 },
{_x -2,_z }
};
//这个是检测周围4个方向的点的墙
int[,] tb1 = new int[4, 2]
{
{ _x,_z + 1},
{_x + 1,_z },
{_x,_z - 1 },
{_x -1,_z }
};
for (int i = 0; i < 4; i++)
{
if (!wallMap.TryGetValue(tb1[i, 0], out Dictionary<int,Transform> m2) || !m2.ContainsKey(tb1[i, 1]))
{
_x = tb[i, 0];
_z = tb[i, 1];
if (list.TryGetValue(_x, out List<int> m3) && m3.Contains(_z))
{
if (!waitChecks.ContainsKey(_x))
waitChecks.Add(_x, new List<int>());
if (!waitChecks[_x].Contains(_z))
waitChecks[_x].Add(_z);
}
else if (!areas.TryGetValue(_x, out List<int> m4) || !m4.Contains(_z))
{
outOfRange = true;
break;
}
}
}
if (outOfRange)
break;
}
}
if (!outOfRange)
{
foreach (var item in areas)
{
foreach (var temp in item.Value)
{
if (!closedMap.ContainsKey(item.Key))
closedMap.Add(item.Key, new List<int>());
closedMap[item.Key].Add(temp);
}
}
}
foreach (var item in list)
{
x = item.Key;
z = item.Value[0];
break;
}
}
}
foreach (var item in closedMap)
{
for (var i = 0; i < item.Value.Count; i++)
{
//封闭区域的每一个点
//foundationMap[item.Key][item.Value[i]].GetComponent<Image>().color = Color.green;
}
}
}
如果事先没有检测点格子,比如只拉了墙,要自动生成房间,这时也可以使用此方法。取出矩形区域内的所有格子放入待检测列表中,也可以计算出所有封闭区域。