XNA A *算法实现过于耗时

时间:2016-06-15 09:34:50

标签: c# performance xna a-star

在我的XNA游戏中,我使用我自己的通用PriorityQueue类实现A *作为敌人行为的一部分。然而,实现过于耗时 - 在游戏时间不到一秒的时间内需要大约5秒的实时时间。究竟是多么耗时,以及如何改变它?

优先级表示为int而不是float,因为当我尝试使用float时,游戏甚至无法启动。

我怀疑操作次数是个问题。在最后一帧的末尾,在我改变了大小之后,被评估的节点的数量(用于找到从(100,100)到(0,0)没有障碍物的路径)是~800或305。网格平方大小从1到5.这改善了帧速率下降,但仍然没有接近平滑。

关于这个主题的大多数文章和堆栈交换问题建议实施一个平局,我尝试将我的h()得分乘以1.1,1.01和1.0001,并且没有任何改变结果。那可能是我误解的东西。

另一个可能的选择是我的PriorityQueue效率不高。不可否认,我不知道如何提高效率并希望得到建议。

敌人成员和追逐法:

    #region data
    private IFocusable Target { get; set; }
    private Map WorldMap { get; set; }
    #endregion

    #region methods
    protected void Chase(GameTime gameTime)
    {
        PriorityQueue<Vector2> openSet = new PriorityQueue<Vector2>();
        List<Vector2> closedSet = new List<Vector2>();
        Dictionary<Vector2, Vector2> cameFrom = new Dictionary<Vector2, Vector2>();
        Dictionary <Vector2, int> gScores = new Dictionary<Vector2, int>();
        openSet.Enqueue(Heuristic(Position, Target.Position), Tools.RoundDown(Position));
        gScores.Add(Position, 0);
        while(openSet.Count != 0)
        {
            Vector2 current = openSet.Dequeue();
            if (current == Tools.RoundDown(Target.Position))
            {
                Position = ReconstructPath(cameFrom, current);
                break;
            }
            closedSet.Add(current);
            List<Vector2> neighbours = WorldMap.GetNeighbours(current, Speed);
            foreach (Vector2 neighbour in neighbours)
            {
                if (closedSet.Contains(neighbour))
                    continue;
                int tenativeGScore = gScores[current] + (int)Vector2.Distance(current, neighbour);
                if (openSet.Contains(neighbour) == -1 || tenativeGScore < gScores[neighbour])
                {
                    cameFrom[neighbour] = current;
                    gScores[neighbour] = tenativeGScore;
                    int fScore = tenativeGScore + Heuristic(neighbour, Target.Position);
                    openSet.Enqueue(fScore, neighbour);
                }
            }
        }
    }

    private Vector2 ReconstructPath(Dictionary<Vector2, Vector2> cameFrom, Vector2 currentNode)
    {
        if (cameFrom[currentNode] == Position)
            return currentNode;
        else
            return ReconstructPath(cameFrom, cameFrom[currentNode]);
    }

    //Heuristic: distance between neighbour and target, rounded down.
    private int Heuristic(Vector2 current, Vector2 goal)
    {
        return (int)Vector2.Distance(current, Tools.RoundDown(goal));
    }
    #endregion
}

的PriorityQueue:

public class PriorityQueue<T> where T : IEquatable<T>
{
    #region data
    private List<Tuple<int, T>> Items { get; set; }
    public int Count {get{return Items.Count;}}
    private bool Sorted { get; set; }
    #endregion

    #region c'tor
    public PriorityQueue()
    {
        this.Items = new List<Tuple<int,T>>();
        this.Sorted = true;
    }
    #endregion

    #region methods
    private int SortingMethod(Tuple<int, T> x, Tuple<int, T> y)
    {
        if (x == null || y == null)
            throw new ArgumentNullException();
        return x.Item1 - y.Item1;
    }
    public void Enqueue(Tuple<int, T> item)
    {
        int index = Contains(item.Item2);
        if (index == -1)
        {
            Items.Add(item);
            Sorted = false;
        }
        else
            Items[index] = item;
    }
    public void Enqueue(int key, T value)
    {
        Enqueue(new Tuple<int,T>(key, value));
    }
    public T Dequeue()
    {
        if(!Sorted)
        {
            Items.Sort(SortingMethod);
            Sorted = true;
        }
        Tuple<int, T> item = Items[0];
        Items.RemoveAt(0);
        return item.Item2;
    }
    public int Contains(T value)
    {
        for (int i = 0; i < Items.Count; i++ )
            if (Items[i].Equals(value))
                return i;
        return -1;
    }
    #endregion
}

Map的相关成员(一个代表敌人导航的方格图的类。我没有来实施一个敌人避开被阻挡的方块的机制。):

    #region data
    private int SquareSize { get; set; }
    private List<Vector2> BlockedSquares { get; set; }
    private Rectangle Bounds { get; set; }
    #endregion

    public List<Vector2> GetNeighbours(Vector2 vector, int speed)
    {
        Vector2[] directions = new Vector2[8];
        List<Vector2> neighbours = new List<Vector2>();
        directions[0] = Tools.RoundDown(Vector2.UnitX);//right
        directions[1] = Tools.RoundDown(Vector2.UnitX);//left
        directions[2] = Tools.RoundDown(Vector2.UnitY);//down
        directions[3] = Tools.RoundDown(Vector2.UnitY);//up
        directions[4] = Tools.RoundDown(Vector2.UnitX + Vector2.UnitY);//down right
        directions[5] = Tools.RoundDown(-Vector2.UnitX + Vector2.UnitY);//down left
        directions[6] = Tools.RoundDown(Vector2.UnitX - Vector2.UnitY);//up right
        directions[7] = Tools.RoundDown(-Vector2.UnitX - Vector2.UnitY);//up left
        for (int i = (int)vector.X - speed; i <= (int)vector.X + speed; i += SquareSize)
        {
            for(int j = (int)vector.Y - speed; j <= (int)vector.Y + speed; j += SquareSize)
            {
                Vector2 point = new Vector2(i, j);
                if (point == vector)
                    continue;
                else if (Vector2.Distance(vector, point) <= speed)
                    neighbours.Add(point);
            }
        }
        return neighbours;
    }

    public Vector2 InSquare(Vector2 vector)
    {
        int x = (int)vector.X, y = (int)vector.Y;
        x -= x % SquareSize;
        y -= y % SquareSize;
        return new Vector2(x, y);
    }

希望这个答案不仅对我有帮助,而且还有许多程序员将来会遇到类似的问题。

提前致谢。

2 个答案:

答案 0 :(得分:1)

尝试将this.isFixedTimeStep = false;放入Initialize()方法。

答案 1 :(得分:0)

减速的原因是使用低效的收容检查。具有快速包含检查的数据类型,如二叉搜索树,HashSets等

在closedSet的情况下,我使用了List而不是HashSet:

List<Vector2> closedSet = new List<Vector2>();

将更改为:

HashSet<Vector2> closedSet = new HashSet<Vector2>();

由于两种类型都有Add和Contains函数,因此无需更改closedSet的任何其他内容。

对于gScores,问题是我使用ContainsKey而不是更高效的TryGetValue。基于this answer

if (openSet.Contains(neighbour) == -1 || tenativeGScore < gScores[neighbour])

需要改为:

float gScore;//Current gScores[neighbour] value, if there's any.
if(gScores.TryGetValue(neighbour, out gScore) || tenativeGScore < gScore)