问题描述
我正在为诸如《帝国时代》之类的游戏进行统一的寻路(我需要这些单位在4个方向上移动,而不是在它们之间堆叠并包围目标) 我的问题如下:如果一个单元被包围并且我将它们指定为目标,我希望这些单元分组关闭。目前,如果目标部队被包围,则所有其他试图到达目标的部队都会发疯。
public void Findpath(Vector3 startPosition,Vector3 targetDestination)
{
Node StartNode = NodeFromWorldPoint(startPosition);
Node TargetNode = NodeFromWorldPoint(targetDestination);
List<Node> OpenList = new List<Node>();
HashSet<Node> ClosedList = new HashSet<Node>();
OpenList.Add(StartNode);
while (OpenList.Count > 0)
{
Node CurrentNode = OpenList[0];
for (int i = 1; i < OpenList.Count; i++)
{
if (OpenList[i].FCost < CurrentNode.FCost || OpenList[i].FCost == CurrentNode.FCost && OpenList[i].hCost < CurrentNode.hCost)
{
CurrentNode = OpenList[i];
}
}
OpenList.Remove(CurrentNode);
ClosedList.Add(CurrentNode);
if (CurrentNode == TargetNode)
{
GetFinalPath(StartNode,TargetNode);
}
foreach (Node NeighborNode in CurrentNode.neighbors)
{
if (NeighborNode.blocked || ClosedList.Contains(NeighborNode) || NeighborNode.occupied)
{
continue;
}
int MoveCost = CurrentNode.gCost + Manhattendistance(CurrentNode,NeighborNode);
if (MoveCost < NeighborNode.gCost || !OpenList.Contains(NeighborNode))
{
NeighborNode.gCost = MoveCost;
NeighborNode.hCost = Manhattendistance(NeighborNode,TargetNode);
NeighborNode.parentNode = CurrentNode;
if (!OpenList.Contains(NeighborNode))
{
OpenList.Add(NeighborNode);
}
}
}
}
}
void GetFinalPath(Node startingNode,Node endNode)
{
List<Node> FinalPath = new List<Node>();
Node CurrentNode = endNode;
while (CurrentNode != startingNode)
{
FinalPath.Add(CurrentNode);
CurrentNode = CurrentNode.parentNode;
}
FinalPath.Reverse();
finalPath = FinalPath;
}
int Manhattendistance(Node nodeA,Node nodeB)
{
int dist = (int)Vector3.distance(nodeA.transform.position,nodeB.transform.position);
return dist;
}
public Node NodeFromWorldPoint(Vector3 worldPosition)
{
var nod = Physics2D.OverlapCircleAll(worldPosition,10);
var nodos = new List<Node>();
foreach (var item in nod)
{
if (item.GetComponent<Node>())
nodos.Add(item.GetComponent<Node>());
}
float dist = Mathf.Infinity;
Node closest = null;
foreach (var item in nodos)
{
var ds = Vector3.distance(worldPosition,item.transform.position);
if (ds < dist)
{
dist = ds;
closest = item;
}
}
return closest;
}
解决方法
前言:我还没有玩过《帝国时代》,所以我不是100%知道我要重现哪种动作(发布视频会有所帮助),但是如果我正确理解了,您会重新寻找像运动的棋盘(没有对角线)。 [编辑] OP提供了可视链接供参考,就像棋盘一样的运动。
您似乎正在尝试实现A *。看看您发布的示例,我发现了一些可能会导致您出现问题的地方。
- 初始化节点
运行搜索后,您是否要初始化/重置节点?我在示例中看不到您要执行的操作,因此,除非您在另一段代码中执行此操作,否则在多次运行搜索时可能会使用旧的成本值。
- 可允许的启发式(曼哈顿距离)
假设您正在尝试实现A *,算法的关键部分是您的启发式算法是可以接受的,这意味着它不会低估成本。您调用了启发式函数Manhattan Distance,但是您的实现实际上是欧几里得距离(请参见区别here)。如果您的单位只能沿四个方向(即上,下,左,右)移动,而不能沿八个方向移动,那么这可能就是您的搜索无法正常工作的原因。
请注意,如果您可以发布有关该行为的视频或更具描述性,那么“发疯”会有所帮助。 [EDIT] OP提供了一个视频来演示行为。
[更新]
如果您尝试获得的行为是尽可能接近,我会考虑实施某种逻辑来确定何时“包围”某个单元,然后通过某种算法(贪婪的方法可能还可以)选择一个最近的节点作为目标。
因此,如果状态看起来像这样:
X X X
X O X
X X X
在X是红色单位而O是蓝色单位的情况下,新的红色单位将包围蓝色单位的下一个“目标”节点看起来像:
T T T T T
T X X X T
T X O X T
T X X X T
T T T T T
T是可能的目标节点
,感谢您的回复。 我没有正确重置节点,所以我解决了这个问题。但是问题依然存在。 这就是我要寻找的动作:(47分钟左右)
https://www.youtube.com/watch?v=TPM0Kn_IS-U
我希望他们不要共享相同的网格空间,但是我希望他们对目标进行加密,并且如果目标附近没有空间,则他们将保持尽可能近的距离。
https://www.youtube.com/watch?v=h7iUghtIoCQ&feature=youtu.be
您可以看到,如果已经有人在里面,他们就不能去另一个部门。如果他们想输入相同的扇区,则需要等待一小段时间并重新计算路径。当所有空间都被占用并且他们找不到通往目标的道路时,问题就开始了,他们开始回头跳舞直到找到空间。我希望,如果没有办法实现其目标,它们至少应保持尽可能近的距离。
再次感谢!英语不是我的主要语言,对不起,如果我不太清楚