我目前正在尝试在Unity和C#中使用一个脚本进行寻路。我正在使用一个SortedSet来获取游戏地图上离目标瓷砖最近的瓷砖。在某些情况下,SortedSet会返回一个不属于集合成员的值作为最小值! 对于这样一个比较特殊的问题,很难贴出一个最小可复制的例子,所以我把使用的脚本贴在下面。
using System.Collections.Generic;
using UnityEngine;
public class Tile : MonoBehaviour
{
//--------------------PATHFINDING-------------------------
//Neighboring tiles stored in a list. Assigned by tile assignment script
public List<Tile> neighbors;
//The tile which leads to this tile when pathfinding. Found during pathfinding.
public Tile previousPathTile { get; private set; }
//The cost of moving across this tile, based on terrain type
public float traversalCost { get; set; }
//The cost of moving through this tile, including the previous tiles already
//on the path. Found during pathfinding.
public float previousTraversalCost { get; private set; }
//The estimated cost from this tile to the end tile. Found during pathfinding.
public float heuristicCost { get; private set; }
//Finds path from current tile to the destination tile
public List<Tile> pathFind( Tile endTile)
{
//The path which is returned by this function
List<Tile> path = new List<Tile>();
//The tiles which have been encountered but not explored
SortedSet<Tile> unexploredTiles = new SortedSet<Tile>(new TileSortOrder());
//The tile which is currently being explored
Tile currentTile;
//Make sure that we're not adding infinity to our traversal cost from the start!
this.previousTraversalCost = 0.0f;
//How much the heuristic cost should guide the search algorithm. Should be set so that the maximum
//multiplier will not force a terrain switch.
float steeringPower = 12.0f;
//Add the starting tile to the unexploredTiles set of tiles
unexploredTiles.Add(this);
//Used to break out of infinite loop on SortedSet error!
int tmp = 0;
//Run the pathfinding algorithm
while (unexploredTiles.Count > 0 && tmp < 250)
{
++tmp;
//---------------- ERROR OCCURS HERE ---------------------------
UnityEngine.Debug.Log(unexploredTiles.Min);
currentTile = unexploredTiles.Min;
if(!unexploredTiles.Contains(unexploredTiles.Min))
{ UnityEngine.Debug.Log("ERROR: unexploredTiles does not contain its own min! " + currentTile + " " + unexploredTiles.Min); }
//Attempting to remove here results in an error code return by the function.
unexploredTiles.Remove(currentTile);
//----------------------------------------------------------------
//If we are on the final tile
if(GameObject.ReferenceEquals(currentTile, endTile))
{
//Rebuild the path from end to start, then reverse.
while (currentTile != null)
{
path.Add(currentTile);
currentTile = currentTile.previousPathTile;
}
path.Reverse();
return path;
}
int neighborsSize = currentTile.neighbors.Count;
for (int i = 0; i < neighborsSize; ++i)
{
//The tile which we are checking the pathfinding cost for
Tile nextTile = currentTile.neighbors[i];
//Get the distance from the next tile to the destination tile only if it hasn't been obtained already.
if(nextTile.heuristicCost < 0.0f)
{
nextTile.heuristicCost = (endTile.gameObject.transform.position - nextTile.gameObject.transform.position).magnitude * steeringPower;
}
//Get the actual traversal cost to the next tile.
float nextTilePreviousTraversalCostFromThisTile = currentTile.previousTraversalCost + currentTile.traversalCost;
//If the cost from this tile to the next tile is less than the previous lowest cost
if ( nextTilePreviousTraversalCostFromThisTile < nextTile.previousTraversalCost )
{
nextTile.previousPathTile = currentTile;
nextTile.previousTraversalCost = nextTilePreviousTraversalCostFromThisTile;
unexploredTiles.Add(nextTile);
}
}
}
return path;
}
}
这里是用来比较两个Tiles的IComparer。
public class TileSortOrder : IComparer<Tile>
{
public int Compare(Tile firstTile, Tile secondTile)
{
UnityEngine.Debug.Log("Comparing: " + firstTile.gameObject.name + " and: " + secondTile.gameObject.name + (GameObject.ReferenceEquals(firstTile, secondTile) ? "They were equal." : "They were not equal.") );
if( GameObject.ReferenceEquals(firstTile, secondTile) )
{
return 0;
}
else
{
float tentativeTraversalCostFirst = firstTile.previousTraversalCost + firstTile.heuristicCost;
float tentativeTraversalCostSecond = secondTile.previousTraversalCost + secondTile.heuristicCost;
return tentativeTraversalCostFirst < tentativeTraversalCostSecond ? -1 : 1;
}
}
}
这里是当SortedSet.Contains()检查失败时产生的一些示例输出。
ERROR: unexploredTiles does not contain its own min! tile1247 (Tile) tile1247 (Tile)
UnityEngine.Debug:Log(Object)
Tile:pathFind(Tile) (at Assets/Scripts/Tiles/Tile.cs:211)
InputRouting:runPathPlan() (at Assets/Scripts/InputRouting.cs:312)
InputRouting:Update() (at Assets/Scripts/InputRouting.cs:291)
如果你能提供任何帮助,我将不胜感激。我很困惑,为什么提供的SortedSet.Min值不能成为集合的成员,我不知道是不是我犯了一个愚蠢的错误。谢谢!谢谢 :-)
不支持更改现有项目的排序值,可能会导致意外行为。
比较器使用以前的TraversalCost,这在代码中是更新的。与其更新traversalCost,不如删除节点并插入一个新节点。