我正在尝试使用我在地形上使用单独脚本生成的路标创建A *寻路算法。 地形上的可穿越和不可穿越的航点由其颜色定义 - 绿色可穿越。
通过以下链接可以看到节点的布局: https://i.stack.imgur.com/JDZdx.jpg
可通过的航路点存储在列表中。
为了启动寻路算法,我创建了一个字典,将每个可遍历的路点存储为唯一键(type = gameobject)。每个键的值是使用距离函数计算的邻居(type = List)。
然后我尝试使用在线参考创建A *算法并根据我的情况调整这些算法。每个部分的注释可以在下面的代码中看到。功能' findPath'是实际的算法(好吧,我最好的尝试)。
void findPath()
{
// Adds start node to open list
// take nodes around start node and add to lista*
open.Add(startNode);
while (open.Count > 0)
{
nodeDistances = 1000;
foreach (GameObject node in open)
{
GCost = Vector3.Distance(startNode.transform.position, node.transform.position);// G distance form node to start
HCost = Vector3.Distance(targetNode.transform.position, node.transform.position); // H distance from node to target
FCost = GCost + HCost;
if (FCost < nodeDistances) // if current node has smaller F cost than the node before that
{
tempGCost = GCost; // Gets the lowest G cost
tempFCost = FCost;
current = node; // Replaces Game Object variable
nodeDistances = FCost;
}
}
if (current.transform.position == targetNode.transform.position)
{
Debug.Log("Goal reached");
break;
}
open.Remove(current);
closed.Add(current);
neighbours = attachedToWaypoint[current];
// path.Add(current);
foreach (GameObject n in neighbours)
{
if (!closed.Contains(n))
{
float g_score = tempGCost + 1; // Takes current node GCost and adds value of 1 for each neighbour
float h_score = Vector3.Distance(n.transform.position, targetNode.transform.position); // Gets distance from each neighbour to the target node
float f_score = g_score + h_score;
if (closed.Contains(n) && f_score >= tempFCost) // If F cost of neighbour is greater than F cost of current node
continue;
if (!open.Contains(n) || f_score < tempFCost) // If 'open' list does not currently contain neighbour or neighbours F score is smaller than that of the current node
{
GCost = g_score;
HCost = h_score;
tempFCost = GCost + HCost; // update current node F score value to get neighbour with the smallest F score
if (!open.Contains(n))
{
open.Add(n); // if neihgbour is not in open list, add to open list
}
}
}
}
}
}
但是,查看存储在关闭列表中的节点,很明显出现了问题 - 封闭列表中的节点图片:http://imgur.com/5cF7voO
有经验的人可以给我一些关于我应该关注的方面的指示吗?另外,你如何使用它来找到最便宜的路径?
任何帮助都将非常感激。
谢谢, 卡尔文
以下完整脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TEST : MonoBehaviour
{
Dictionary<GameObject, List<GameObject>> attachedToWaypoint = new Dictionary<GameObject, List<GameObject>>();
List<GameObject> gameObjectForDic = new List<GameObject>();
GameObject[] waypoints;
List<List<GameObject>> nodeData = new List<List<GameObject>>();
List<GameObject> neighbours = new List<GameObject>(); // Not currently used
public GameObject enemy;
public GameObject player;
GameObject startNode;
GameObject targetNode;
List<GameObject> open = new List<GameObject>();
List<GameObject> closed = new List<GameObject>();
float distanceToEnemy;
float distanceToPlayer;
float tempFCost;
float FCost;
float GCost;
float HCost;
float tempGCost;
float accumulatedCost;
float accumulatedCostTemp;
float nodeDistances;
GameObject current;
public GameObject parent;
List<GameObject> path = new List<GameObject>();
// Use this for initialization
void Start()
{
distanceToEnemy = 1000;
distanceToPlayer = 1000;
nodeDistances = 10000;
waypoints = GameObject.FindGameObjectsWithTag("Waypoint");
storeNodesInDictionary();
findPath();
// findPathtoPlayer();
testing();
}
void storeNodesInDictionary()
{
for (int i = 0; i < waypoints.Length; i++)
{
nodeData.Add(new List<GameObject>());
float distEnemy = Vector3.Distance(enemy.transform.position, waypoints[i].transform.position); // Closest node to enemy
if (distEnemy < distanceToEnemy)
{
startNode = waypoints[i];
distanceToEnemy = distEnemy;
}
float distPlayer = Vector3.Distance(player.transform.position, waypoints[i].transform.position);// Closest node to player
if (distPlayer < distanceToPlayer)
{
targetNode = waypoints[i];
distanceToPlayer = distPlayer;
}
for (int j = 0; j < waypoints.Length; j++)
{
float distanceSqr = (waypoints[i].transform.position - waypoints[j].transform.position).sqrMagnitude; // Gets distance values
if (distanceSqr < 60 && waypoints[i] != waypoints[j]) // Is waypoints are within a spcific distance
{
nodeData[i].Add(waypoints[j]);
}
}
attachedToWaypoint.Add(waypoints[i], nodeData[i]); // Adds parent node and neighbouring nodes within a 3x3 grid
}
}
void findPath()
{
// Adds start node to open list
// take nodes around start node and add to lista*
open.Add(startNode);
while (open.Count > 0)
{
nodeDistances = 1000;
foreach (GameObject node in open)
{
GCost = Vector3.Distance(startNode.transform.position, node.transform.position);// G distance form node to start
HCost = Vector3.Distance(targetNode.transform.position, node.transform.position); // H distance from node to target
FCost = GCost + HCost;
if (FCost < nodeDistances) // if current node has smaller F cost than the node before that
{
tempGCost = GCost; // Gets the lowest G cost
tempFCost = FCost;
current = node; // Replaces Game Object variable
nodeDistances = FCost;
}
}
if (current.transform.position == targetNode.transform.position)
{
Debug.Log("Goal reached");
break;
}
open.Remove(current);
closed.Add(current);
neighbours = attachedToWaypoint[current];
// path.Add(current);
foreach (GameObject n in neighbours)
{
if (!closed.Contains(n))
{
float g_score = tempGCost + 1; // Takes current node GCost and adds value of 1 for each neighbour
float h_score = Vector3.Distance(n.transform.position, targetNode.transform.position); // Gets distance from each neighbour to the target node
float f_score = g_score + h_score;
if (closed.Contains(n) && f_score >= tempFCost) // If F cost of neighbour is greater than F cost of current node
continue;
if (!open.Contains(n) || f_score < tempFCost) // If 'open' list does not currently contain neighbour or neighbours F score is smaller than that of the current node
{
GCost = g_score;
HCost = h_score;
tempFCost = GCost + HCost; // update current node F score value to get neighbour with the smallest F score
if (!open.Contains(n))
{
open.Add(n); // if neihgbour is not in open list, add to open list
}
}
}
}
}
}
}
答案 0 :(得分:0)
一些建议:
Closed
的良好数据结构是HashSet,这肯定会加快您的代码速度并且不需要太多更改。
不幸的是Open
的正确数据结构是priority queue,但是c#没有内置的数据结构,如果您使用可以提供最佳性能的第三方实现,那么f#成本将是您的首要任务。否则你需要自己编写。
至于你的封闭列表图片看起来并不坏,但是从我能够弄清楚错误可能是以下几行:
GCost = Vector3.Distance(startNode.transform.position, node.transform.position); // G distance from node to start
g cost应该是从startNode到节点的实际距离,但是您使用相同的函数来计算用于计算h的g。这就像在整个时间里使用两条直线一样,如果你想象它会如何通过一条直线路径绕过它的锯齿形路径,它会一直沿着曲折的路径走下去,一直认为它是直的从startNode到当前节点。
以下是我在调查时对代码所做的工作,你可以用它做什么。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Some.Namespace.That.Gets.PriorityQueue;
public class TEST: MonoBehaviour {
Dictionary<GameObject, List<GameObject>> attachedToWaypoint = new Dictionary<GameObject, List<GameObject>>();
GameObject[] waypoints;
GameObject startNode;
GameObject targetNode;
GameObject current;
PriorityQueue<float, GameObject> open = new PriorityQueue<float, GameObject>();
HashSet<GameObject> closed = new HashSet<GameObject>();
List<GameObject> path = new List<GameObject>();
float distanceToEnemy;
float distanceToPlayer;
float FCost;
float GCost;
float HCost;
float nodeDistances;
public GameObject enemy;
public GameObject player;
public GameObject parent;
// Use this for initialization
void Start() {
distanceToEnemy = 1000;
distanceToPlayer = 1000;
nodeDistances = 10000;
waypoints = GameObject.FindGameObjectsWithTag("Waypoint");
storeNodesInDictionary();
findPath();
// findPathtoPlayer();
testing();
}
void storeNodesInDictionary() {
for (int i = 0; i < waypoints.Length; i++) {
var waypoint = waypoints[i];
var nodeData = new List<GameObject>();
var waypointPosition = waypoint.transform.position;
float distEnemy = Vector3.Distance(enemy.transform.position, waypointPosition); // Closest node to enemy
if (distEnemy < distanceToEnemy) {
startNode = waypoint;
distanceToEnemy = distEnemy;
}
float distPlayer = Vector3.Distance(player.transform.position, waypointPosition); // Closest node to player
if (distPlayer < distanceToPlayer) {
targetNode = waypoint;
distanceToPlayer = distPlayer;
}
for (int j = 0; j < waypoints.Length; j++) {
if (i == j)
continue;
var otherWaypoint = waypoints[j];
float distanceSqr = (waypointPosition - otherWaypoint.transform.position).sqrMagnitude; // Gets distance values
if (distanceSqr < 60) // Is waypoints are within a spcific distance
{
nodeData.Add(otherWaypoint);
}
}
attachedToWaypoint.Add(waypoint, nodeData); // Adds parent node and neighbouring nodes within a 3x3 grid
}
}
void findPath() {
var startPosition = startNode.transform.position;
var targetPosition = targetNode.transform.position;
open.Push(Vector3.Distance(startPosition, targetPosition), startNode);
while (open.Count > 0) {
FCost = open.Pop(out current);
var currentPosition = current.transform.position;
if (currentPosition == targetPosition) {
Debug.Log("Goal reached");
break;
}
HCost = Vector3.Distance(currentPosition, targetPosition);
GCost = FCost - HCost;
closed.Add(current);
var neighbours = attachedToWaypoint[current];
// path.Add(current);
foreach(GameObject n in neighbours) {
if (!closed.Contains(n) && !open.Contains(n)) // If 'open' list does not currently contain neighbour or neighbours F score is smaller than that of the current node
{
var neighbourPosition = n.transform.position;
var neighbourFCost = GCost + Vector3.Distance(currentPosition, neighbourPosition) + Vector3.Distance(neighbourPosition, targetPosition)
open.Push(neighbourFCost, n); // if neighbour is not in open list, add to open list
}
}
}
}
}