我正在制作一个基于转弯的六角网格游戏。玩家选择单位并在十六进制网格中移动它们。网格中的每个图块都是特定的地形类型(例如沙漠,丘陵,山脉等),每个单位类型在地形上移动时具有不同的能力(例如,一些可以轻松地在山上移动,一些有困难而且有些根本没有。)
每个单位都有一个移动值,每个单位根据其地形类型和单位类型进行一定程度的移动。例如,它需要一个坦克1才能越过沙漠,4个越过沼泽并且无法在山上移动。飞行单位以1的成本移动到所有地方。
我遇到的问题是,当选择一个单位时,我想突出显示它周围的区域,显示它可以移动的位置,这意味着计算出通过周围六边形的所有可能路径,每个路径将采取多少移动和根据该信息点亮瓷砖。
我使用递归函数工作,发现计算花了太长时间,我将函数移动到一个线程中,这样它就不会阻塞游戏,但是线程计算可移动性需要大约2秒钟移动8的单位面积。 它有超过一百万次递归,这显然是有问题的。
我想知道是否有人对如何优化此问题有一个聪明的想法。
这是我目前正在使用的递归函数(它的C#btw):
private void CalcMoveGridRecursive(int nCenterIndex, int nMoveRemaining)
{
//List of the 6 tiles adjacent to the center tile
int[] anAdjacentTiles = m_ThreadData.m_aHexData[nCenterIndex].m_anAdjacentTiles;
foreach(int tileIndex in anAdjacentTiles)
{
//make sure this adjacent tile exists
if(tileIndex == -1)
continue;
//How much would it cost the unit to move onto this adjacent tile
int nMoveCost = m_ThreadData.m_anTerrainMoveCost[(int)m_ThreadData.m_aHexData[tileIndex].m_eTileType];
if(nMoveCost != -1 && nMoveCost <= nMoveRemaining)
{
//Make sure the adjacent tile isnt already in our list.
if(!m_ThreadData.m_lPassableTiles.Contains(tileIndex))
m_ThreadData.m_lPassableTiles.Add(tileIndex);
//Now check the 6 tiles surrounding the adjacent tile we just checked (it becomes the new center).
CalcMoveGridRecursive(tileIndex, nMoveRemaining - nMoveCost);
}
}
}
在递归结束时,m_lPassableTiles包含一个单元可能达到的所有图块的索引列表,并使它们发光。
一切正常,只需要太长时间。有没有人知道更好的方法呢?
答案 0 :(得分:0)
如您所知,使用递归函数,您希望尽可能简化问题。这仍然看起来像是试图立刻咬掉太多。几个想法:
尝试使用HashSet
结构来存储m_lPassableTiles
?您可以通过这种方式避免Contains
条件,这通常是一项昂贵的操作。
我没有彻底测试过这个问题的逻辑,但你可以在foreach
循环之前设置一个基本案例吗?即,nMoveRemaining == 0
?
在不知道您的程序是如何在内部设计的情况下,我希望m_anAdjacentTiles
无论如何都只包含现有的切片,因此您可以取消该检查(tileIndex == -1
)。不是一个巨大的性能提升,但使您的代码更简单。
顺便说一下,我认为这样做的游戏,如文明V,只计算移动成本,因为用户建议将单位移动到某个位置。换句话说,您选择一个图块,它会显示它将采取多少动作。这是一种更有效的操作。
当然,当你移动一个单位时,周围的土地被揭示 - 但我认为只有单位可以在一个“转弯”中移动才能显示土地,然后在移动时会显示更多。如果您选择将几个转弯移动到未知区域,最好仔细观察或一次转动一圈。 :)
(适用后来...)强>
等等,一百万次递归?是的,我认为这是正确的数学:6^8
(8是可用的运动) - 但你的网格真的那么大吗? 1000×1000?该单位实际可以穿过多少个瓷砖?假设不同的地形类型,在任何给定方向上平均可能是4或5?
如果我错了(因为我不知道你的底层设计),请纠正我,但我认为有一些重叠...主要重叠。它正在检查已检查的相邻瓷砖的相邻瓷砖。我认为唯一可以避免无限递归的事情就是检查剩下的动作。
将图块添加到m_lPassableTiles
后,将其从接收到您的函数中的任何相邻图块列表中删除。你在Contains
的行中做了类似的事情......如果你附上if
语句来包含你的递归调用怎么办?这应该会将你的递归调用从最多100万+减少到数千......我想。
答案 1 :(得分:0)
感谢大家的投入。我通过用Dijkstra's Algorithm替换Recursive函数解决了这个问题,它完美无缺。