如果在阅读此篇文章的时候,不了解地牢算法的建议去看一下,这里不介绍地牢算法了,只是谈论一下地牢算法不好的地方,如何去扩展和优化地牢算法,下面是地牢算法的链接:https://www.cnblogs.com/sufferingStriver/p/8834862.html
看过地牢算法以后,现在提出几个需求,比如必须要生成有n个房间的一个地图,但是地牢算法会在计算的时候将不满足条件的房间去掉,还有比如有一个要求有一些房间必须只有一个入口,地牢算法在这个时候是没有办法搞定这种需求,因为最小生成树是没有办法解决这种特殊的点,所以需要想办法拓展一下。
这里需要优化分散房间位置的算法,最先想到的方法就是一个随机区域之内随机放n个房间,如果和之前设置的房间位置碰撞了就重新随机,这样的话怎么去判断随机区域大小,如果随机区域直接最大,之前不管多少房间这样一定不会重叠的,但是这样指定不行,房间密度会太散了,一个房间到另外一个房间可能太远了,这样指定不行,所以怎么样可以判断最佳的随机区域大小?第一个想法就是所有房间宽加起来就是随机区域的最大宽,所有高加起来是就是随机区域的最大高,如图所示:
但是这样的随机区域就是最佳吗?这个最坏情况也是一定可以放下所有房间的,可以看出来这个区域也太大了,这样需要想办法优化这个随机区域大小,于是经过一番思考,可以这样优化随机区域的大小,就是找到n房间中最大宽度值和最大高度值,上面的图最大高度是3房间,最大宽度是2房间,如下图所示:
房间1放在(MaxWidth,MaxHeight),房间2,3放在(2*MaxWidth,2*MaxHeight),房间4,5,6放在(3*MaxWidth,3*MaxHeight),房间7,8,9,10放在(4*MaxWidth,4*MaxHeight),房间11,12,13,14,15放在(5*MaxWidth,5*MaxHeight),这样就算出现最坏情况,也没有问题的,慢慢增加随机区域,为什么一定可以容下所有房间?大家可以自己思考一下,代码如下:
bool SetRandomRoom(int i, int MaxWidth, int MaxHeight,uint Count)
{
int x = Random.Range(0, MaxWidth);
int y = Random.Range(0, MaxHeight);
Rooms[i].SetLocation(x, y);
if(Count == 0x0fff)//栈溢出保护
return false;
for (int j = 0; j < i - 1; j++)
{
if (isOverLap(Rooms[i], Rooms[j]))//如果重叠
{
SetRandomRoom(i, MaxWidth, MaxHeight, Count+1);
break;
}
}
return true;
}
void SetMapPoint()
{
int i,j,k = 0,k1=1,MaxHeight=0,MaxWidth=0,MaxWidthCell=0,MaxHeightCell=0;
for (i = 0; i < Rooms.Count; i++)//打散房间
{
for (j = i + 1; j < Rooms.Count; j++)
{
if (Random.Range(0, 2) == 1)
SwapRoomInfo(i, j);
}
if (MaxWidthCell < Room[i].Rect.Width)
MaxWidthCell = Room[i].Rect.Width;
if (MaxHeightCell < Room[i].Rect.Height)
MaxHeightCell = Room[i].Rect.Height;
}
for (i = 0; i < Room.Count; i++)
{
if (k == i)//控制房间密度
{
k = k + k1;
k1 = k1 + 1;
MaxWidth = MaxWidth + MaxWidthCell;
MaxHeightCell = MaxHeight + MaxHeightCell;
}
if (SetRandomRoom(i, MaxWidth, MaxHeight, 0) == false)
{
//TODO 防止意外(回滚所有随机)
k = 0;k1 = 1;
MaxWidth = MaxHeight = 0;
i = 0;
}
}
}
这样就可以把房间位置散开,而且比较密集。之后最小生成树连接这些点,然后如何去添加只有单个入口的房间呢,需要在这个最小生成树里用贪心算法去添加这些点,随机这些特殊点(确保这些和之前的房间和路线碰撞),然后连接这些特殊点,但是这里有一个问题,因为随机函数的问题,可能多次随机都有无法找到连接的路线,这样的话尝试了一定次数之后,这样我们需要遍历随机范围将将剩余的特殊点都连接进去。
如果有更好的方法或者疑问,大家可以留言讨论一下,如果需要demo工程的小伙伴,请大家留言一下~,笔者会抽空用unity写一个demo出来提供下载。