每次找到新路径时,A * Pathfinder会来回移动

时间:2013-12-17 15:53:38

标签: c# xna pathfinder

我正在尝试在C#XNA 4.0中为我的游戏制作一个A *探路者。

如果探路者执行一次,那么怪物会正确移动。但是当怪物移动时,探路者再次执行,并使用敌人的当前位置。 路径探测器需要大约半秒到一秒才能完成。因此,当探路者完成并返回新路径时,怪物已经离开了同时使用的位置。所以现在怪物会全程移回到所使用的位置,只是为了开始新的路径。

我如何解决这个问题?

我不知道你需要什么。如果它是你需要看到的探路者代码,那么我会发布它,但这里是僵尸类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.IO;

namespace *Private name*
{
    class Zombie : Obj
    {
        ContentManager Content;
        private Vector2 dest;
        private bool[,] map;
        private List<Note> path = new List<Note>();
        private int PathIndex = 0;
        private bool queued = false;
        private int queTimer = 0;
        private int queTime = 10;
        private bool finding = false;
        private bool walkable = false;
        private Thread t;

        public Zombie(Vector2 pos, string sprName, int MaxHP, int HP, int damage) : base(pos, sprName, MaxHP, HP)
        {
            position = pos;
            spriteName = sprName;

            maxHealth = MaxHP;

            if (HP > maxHealth)
                Health = maxHealth;
            else if (HP < 0)
                Health = 1;
            else
                Health = HP;

            maxHealth = 10;
            Health = 10;

            solid = true;
            speed = 1.0f;
            dest = position;
            this.damage = damage;
        }

        //Loads the content and sets some values
        public override void LoadContent(ContentManager content)
        {
            spriteTexture = content.Load<Texture2D>(spriteName);
            spriteRectangle = new Rectangle((int)position.X, (int)position.Y, spriteTexture.Width, spriteTexture.Height);
            centerPosRec.X = (int)position.X + (spriteRectangle.Height / 2);
            centerPosRec.Y = (int)position.Y + (spriteRectangle.Width / 2);
            Content = content;
        }

        public override void Update()
        {
            if (!alive) return;

            if (Health <= 0)
                alive = false;

            if (attackCoolDown > 0)
                attackCoolDown--;

            if (queTimer > queTime)
            {
                queTimer = 0;
                NewSetPath();
            }
            else
            { queTimer++; }

            MoveToDestination();

            spriteRectangle.X = (int)position.X;
            spriteRectangle.Y = (int)position.Y;

            centerPosRec.X = (int)position.X + (spriteRectangle.Height / 2);
            centerPosRec.Y = (int)position.Y + (spriteRectangle.Width / 2);
        }

        //Here the program finds the path
        private void NewSetPath()
        {
            if (t != null)
                if (t.IsAlive == true)
                    return;

            dest = Player.player1.centerPosRec;

            if (!finding)
            {
                finding = true;
                t = new Thread(NewFindPath);
                t.Start();
            }

            if (!t.IsAlive && finding)
            {
                t.Abort();
                finding = false;
                queued = false;
                PathIndex = 0;
            }
        }
        //Here it also finds the path
        private void NewFindPath()
        {
            map = MyPathFinder.writeMap();
            MyPathFinder finder;

            finder = new MyPathFinder(map);
            path = finder.findPath(this.centerPosRec, this.dest);
        }
        //Moves to the next point/note in the path list
        private void MoveToDestination()
        {  
            if (path == null)
            {
                return;
            }

            if (PathIndex < path.Count)
            {
                if (stepToPoint(path[PathIndex]))
                {
                    PathIndex++;
                }
                else
                {
                    PushTo(speed, rotation);
                }
            }
            else if (path.Count >= 0)
            {
                path = null;
                PathIndex = 0;
                queued = false;
                dest = Player.player1.position;
                NewSetPath();
            }
        }

        //Checkes for collition and distance to the point
        private bool stepToPoint(Note note)
        {
            if (PointDist(centerPosRec.X, centerPosRec.Y, note.posRectangle.Y + (pathFinder.gridSize / 2), note.posRectangle.X + (pathFinder.gridSize / 2)) < pathFinder.gridSize / 2)
            {
                speed = 0;
                return true;
            }
            rotation = Point_Direction(centerPosRec.X, centerPosRec.Y, note.posRectangle.Y + (pathFinder.gridSize / 2), note.posRectangle.X + (pathFinder.gridSize / 2));
            speed = 2f;

            return false;
        }

        public override void Draw(SpriteBatch spriteBatch)
        {
            try
            {
                Vector2 center = new Vector2(spriteTexture.Width / 2, spriteTexture.Height / 2);

                foreach (Note n in path)
                {
                    spriteBatch.Draw(Content.Load<Texture2D>("OfficeWall"), new Vector2(n.position.Y * 32, n.position.X * 32), null, Color.White, 0, center, scale, SpriteEffects.None, 0);
                }
            }
            catch
            { }

            base.Draw(spriteBatch);

            try
            {
                spriteBatch.DrawString(Content.Load<SpriteFont>("HUDFont"), PathIndex + "/" + path.Count, new Vector2(500, 500), Color.White);
            }
            catch
            { }
        }
        //Gets distance between points
        public static float PointDist(float x1, float y1, float x2, float y2)
        { 
            float xRect = (x1 -x2) * (x1 - x2);
            float yRect = (y1 - y2) * (y1 - y2);

            double hRect = xRect + yRect;

            float dist = (float)Math.Sqrt(hRect);
            return dist;
        }
        //Movement
        public override void PushTo(float pix, float dir)
        {
            float newX = (float)Math.Cos(MathHelper.ToRadians(dir));
            float newY = (float)Math.Sin(MathHelper.ToRadians(dir));
            newX *= pix;
            newY *= pix;

            if (!Collision(new Vector2(newX, newY)))
            {
                base.PushTo(pix, dir);
            }
            else if (!Collision(new Vector2(0, newY)))
            {
                this.position += new Vector2(0, newY);
            }
            else if (!Collision(new Vector2(newX, 0)))
            {
                this.position += new Vector2(newX, 0);
            }
        }

        public override bool Collision(Vector2 pos)
        {
            Rectangle area = new Rectangle(spriteRectangle.X, spriteRectangle.Y, spriteRectangle.Width, spriteRectangle.Height);
            area.X += (int)pos.X;
            area.Y += (int)pos.Y;

            foreach (Obj o in Items.objList)
            {
                if (o.solid && o.alive && o != this && o.GetType() != typeof(Zombie))
                {
                    if (area.Intersects(Player.player1.spriteRectangle))
                    {
                        if (Player.player1.alive == true && attackCoolDown == 0)
                        {
                            this.attackCoolDown = 55;

                            Player.player1.Health -= damage;
                            Player.player1.regenAfterDamageTimer = 0;
                            Player.player1.PushTo(5f, rotation);
                            return true;
                        }
                    }

                    if (o.spriteRectangle.Intersects(area))
                    {
                        return true;
                    }
                }
            }

            return false;
        }
    }
}

好的,这是探路者,它没有完全完成,我知道我必须移动if语句来检查是否正在检查结束位置。我通过更新每个刻度来加快速度,现在它工作得更好。现在需要一个毫秒但是这里是:D

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.IO;

namespace *Private name*
{
    class MyPathFinder
    {
        List<Note> path = new List<Note>();
        List<Note> openList = new List<Note>();
        List<Note> closedList = new List<Note>();
        Note start;
        Note endPos;

        private Note[,] map;
        public static int GridSize = 32;
        bool NorthRight = false, NorthLeft = false, SouthRight = false, SouthLeft = false, EastRight = false, EastLeft = false, WestRight = false, WestLeft = false;
        //string values = "";

        public MyPathFinder(bool[,] cMap)
        {
            map = new Note[cMap.GetLength(0), cMap.GetLength(1)];
            for (int x = 0; x < cMap.GetLength(0); x++)
            {
                for (int y = 0; y < cMap.GetLength(1); y++)
                {
                    Rectangle rec = new Rectangle();
                    rec.Width = rec.Height = pathFinder.gridSize;
                    rec.X = x * pathFinder.gridSize;
                    rec.Y = y * pathFinder.gridSize;

                    map[x, y] = new Note();
                    map[x, y].walkable = cMap[x, y];
                    map[x, y].posRectangle = rec;
                    map[x, y].position = new Point(x, y);
                }
            }


        }

        public static bool[,] writeMap(params Obj[] objEx)
        {

            bool[,] cMap = new bool[Convert.ToInt16(Game1.room.Width / pathFinder.gridSize), Convert.ToInt16(Game1.room.Height / pathFinder.gridSize)];
            //string output2 = "";

            //loop through rows
            for (int x = 0; x < cMap.GetLength(0); x++)
            {
                for (int y = 0; y < cMap.GetLength(1); y++)
                {
                    Rectangle rec = new Rectangle();
                    rec.Width = rec.Height = pathFinder.gridSize;
                    rec.X = y * pathFinder.gridSize; 
                    rec.Y = x * pathFinder.gridSize;

                    //output2 += Convert.ToString("{" + y + "," + x);

                    //If collision with grid then grid is unwalkable
                    foreach (Obj o in Items.objList)
                    {
                        if (o.spriteRectangle.Intersects(rec) && o.alive && o.solid && !objEx.Contains<Obj>(o) && o.GetType() != typeof(Zombie) && o.GetType() != typeof(Player)) 
                        {
                            cMap[x, y] = false;
                            break;
                        }
                        else
                        {
                            cMap[x, y] = true;

                        }
                    }
                    //output2 += " = " + cMap[x, y] + "} -- ";
                }
                //output2 += "\r\n";
            }
            //File.WriteAllText("DEBUG-Walkable.txt", output2);

            return cMap;
        }

        public List<Note> findPath(Vector2 pos, Vector2 dest)
        {
            string wtf = "";
            int num = 0;
            path.Clear();
            openList.Clear();
            closedList.Clear();

            Point startPos = new Point(Convert.ToInt16(Math.Floor((double)(pos.Y / GridSize))), Convert.ToInt16(Math.Floor((double)(pos.X / GridSize))));
            Point end = new Point(Convert.ToInt16(Math.Floor((double)(dest.Y / GridSize))), Convert.ToInt16(Math.Floor((double)(dest.X / GridSize))));

            start = map[startPos.X, startPos.Y];
            endPos = (map[end.X, end.Y]);

            openList.Add(map[startPos.X, startPos.Y]);

            while (!endPos.closed)
            {
                num++;
                openList = openList.OrderBy(p => p.F).ToList();
                Note temp = null;

                foreach (Note n in openList)
                {
                    if (!closedList.Contains(n))
                    {
                        wtf += "number " + num + " = " + n.position.X + ", " + n.position.Y + "\r\n";
                        parenting(n.position, end);
                        n.closed = true;
                        temp = n;
                        break;
                    }
                }

                if (temp != null)
                {
                    closedList.Add(temp);
                    openList.Remove(temp);
                }

                //File.WriteAllText("checking.txt", wtf);
                //File.WriteAllText("listContent.txt", outp);
                //File.WriteAllText("listCount.txt", openList.Count.ToString());
            }

            return path;
        }

        private void parenting(Point pos, Point dest)
        {
            checkNorth(pos, dest);
            checkSouth(pos, dest);
            checkEast(pos, dest);
            checkWest(pos, dest);

            //File.WriteAllText("DEBUGpos[" + pos.Y + ", " + pos.X + "].txt", values);

            //if (NorthLeft && NorthRight && SouthLeft && SouthRight && EastLeft && EastRight && WestLeft && WestRight)
            //   Settings.exit = true;

        }

        private int GetH(Point v, Point endPos)
        {
            Point diff = new Point(v.X - endPos.X, v.Y - endPos.Y);
            if (diff.X < 0) { diff.X *= 1; }
            if (diff.Y < 0) { diff.Y *= 1; }

            return Convert.ToInt16(diff.X + diff.Y);
        }

        private void checkNorth(Point pos, Point dest)
        {
            Point p = new Point(pos.X - 1, pos.Y);
            if (map.XInRange(p.X))
            {
                if (map[p.X, p.Y].walkable && !map[p.X, p.Y].closed)
                {
                    //values += p.X.ToString() + "," + p.Y.ToString() + " - " + map[p.X, p.Y].walkable + "\r\n";
                    if (map[p.X, p.Y] == endPos)
                    {
                        //File.WriteAllText("EndFound.txt", p.X + " - " + p.Y);
                        endPos.parent = pos;
                        endPos.closed = true;
                        closedList.Add(endPos);
                        path.Add(map[p.X, p.Y]);

                        while (!path.Contains(start))
                        {
                            path.Add(map[path[path.Count - 1].parent.X, path[path.Count - 1].parent.Y]);
                        }

                        path.Reverse();
                    }
                    else
                    {
                        if (map[p.X, p.Y].open == true)
                        {
                            if (map[pos.X, pos.Y].G + 10 < map[p.X, p.Y].G)
                            {
                                Settings.exit = true;
                            }
                        }
                        else
                        {
                            map[p.X, p.Y].open = true;
                            map[p.X, p.Y].parent = pos;
                            map[p.X, p.Y].H = GetH(p, dest);
                            map[p.X, p.Y].G = map[pos.X, pos.Y].G + 10;
                            map[p.X, p.Y].F = map[p.X, p.Y].G + map[p.X, p.Y].H;

                            NorthLeft = (map[p.X, p.Y - 1].walkable == true) ? true : false;
                            NorthRight = (map[p.X, p.Y + 1].walkable == true) ? true : false;

                            openList.Add(map[p.X, p.Y]);

                        }
                    }

                }
                else
                {
                    NorthLeft = false;
                    NorthRight = false;
                }
            }
            else
            {
                SouthLeft = false;
                SouthRight = false;

            }
            //values += NorthLeft.ToString() + " " + NorthRight.ToString() + "\r\n";
        }

        private void checkSouth(Point pos, Point dest)
        {
            Point p = new Point(pos.X + 1, pos.Y);

            if (p.X < 0)
            {
                SouthLeft = false;
                SouthRight = false;
                return;
            }

            //values += p.X.ToString() + "," + p.Y.ToString() + " - " + map[p.X, p.Y].walkable + "\r\n";
            if (Enumerable.Range(0, map.GetLength(0)).Contains(p.X))
            {
                if (map[p.X, p.Y].walkable && !map[p.X, p.Y].closed)
                {
                    if (map[p.X, p.Y] == endPos)
                    {
                        //File.WriteAllText("EndFound.txt", p.X + " - " + p.Y);
                        endPos.parent = pos;
                        endPos.closed = true;
                        closedList.Add(endPos);
                        path.Add(map[p.X, p.Y]);

                        while (!path.Contains(start))
                        {
                            path.Add(map[path[path.Count - 1].parent.X, path[path.Count - 1].parent.Y]);
                        }

                        path.Reverse();
                    }
                    else
                    {
                        if (map[p.X, p.Y].open == true)
                        {
                            if (map[pos.X, pos.Y].G + 10 < map[p.X, p.Y].G)
                            {

                            }
                        }
                        else
                        {
                            map[p.X, p.Y].open = true;
                            map[p.X, p.Y].parent = pos;
                            map[p.X, p.Y].H = GetH(p, dest);
                            map[p.X, p.Y].G = map[pos.X, pos.Y].G + 10;
                            map[p.X, p.Y].F = map[p.X, p.Y].G + map[p.X, p.Y].H;

                            if (Enumerable.Range(0, map.GetLength(1)).Contains(p.Y + 1))
                                SouthLeft = (map[p.X, p.Y + 1].walkable == true) ? true : false;
                            if (Enumerable.Range(0, map.GetLength(1)).Contains(p.Y - 1))
                                SouthRight = (map[p.X, p.Y - 1].walkable == true) ? true : false;

                            openList.Add(map[p.X, p.Y]);
                        }
                    }
                }
                else
                {
                    SouthLeft = false;
                    SouthRight = false;

                }
            }
            else
            {
                SouthLeft = false;
                SouthRight = false;

            }
            //values += SouthLeft.ToString() + " " + SouthRight.ToString() + "\r\n";

        }

        private void checkEast(Point pos, Point dest)
        {
            Point p = new Point(pos.X, pos.Y + 1);
            //File.WriteAllText("testEastpos.txt", p.X + " - " + p.Y + " -- " + map[p.X, p.Y].walkable);
            //values += p.X.ToString() + "," + p.Y.ToString() + " - " + map[p.X, p.Y].walkable + "\r\n";
            if (Enumerable.Range(0, map.GetLength(1)).Contains(p.Y))
            {
                if (map[p.X, p.Y].walkable && !map[p.X, p.Y].closed)
                {
                    if (map[p.X, p.Y] == endPos)
                    {
                        //File.WriteAllText("EndFound.txt", p.X + " - " + p.Y);
                        endPos.parent = pos;
                        endPos.closed = true;
                        closedList.Add(endPos);
                        path.Add(map[p.X, p.Y]);

                        while (!path.Contains(start))
                        {
                            path.Add(map[path[path.Count - 1].parent.X, path[path.Count - 1].parent.Y]);
                        }

                        path.Reverse();
                    }
                    else
                    {
                        if (map[p.X, p.Y].open == true)
                        {
                            if (map[pos.X, pos.Y].G + 10 < map[p.X, p.Y].G)
                            {

                            }
                        }
                        else
                        {
                            map[p.X, p.Y].open = true;
                            map[p.X, p.Y].parent = pos;
                            map[p.X, p.Y].H = GetH(p, dest);
                            map[p.X, p.Y].G = map[pos.X, pos.Y].G + 10;
                            map[p.X, p.Y].F = map[p.X, p.Y].G + map[p.X, p.Y].H;

                            if (p.X - 1 >= 0)
                                EastLeft = (map[p.X - 1, p.Y].walkable == true) ? true : false;
                            else
                                EastLeft = false;

                            if (Enumerable.Range(0, map.GetLength(0)).Contains(p.Y))
                            {
                                EastRight = (map[p.X + 1, p.Y].walkable == true) ? true : false;
                            }
                            else
                            { EastRight = false; }

                            openList.Add(map[p.X, p.Y]);
                        }
                    }
                }
                else
                {
                    SouthLeft = false;
                    SouthRight = false;

                }
            }
            else
            {

                EastLeft = false;
                EastRight = false;
            }

            try
            {
                //File.WriteAllText("testEastSidepos.txt", (p.X - 1) + " - " + p.Y + " -- " + map[p.X - 1, p.Y].walkable + "\r\n" +
                //                                        (p.X + 1) + " - " + p.Y + " -- " + map[p.X + 1, p.Y].walkable);
            }
            catch
            { }

            //values += EastLeft.ToString() + " " + EastRight.ToString() + "\r\n";
        }

        private void checkWest(Point pos, Point dest)
        {
            Point p = new Point(pos.X, pos.Y - 1);

            if (Enumerable.Range(0, map.GetLength(1)).Contains(p.Y))
            {
                //values += p.X.ToString() + "," + p.Y.ToString() + " - " + map[p.X, p.Y].walkable + "\r\n";

                if (map[p.X, p.Y].walkable && !map[p.X, p.Y].closed)
                {

                    if (map[p.X, p.Y] == endPos)
                    {
                        //File.WriteAllText("EndFound.txt", p.X + " - " + p.Y);
                        endPos.parent = pos;
                        endPos.closed = true;
                        closedList.Add(endPos);
                        path.Add(map[p.X, p.Y]);

                        while (!path.Contains(start))
                        {
                            path.Add(map[path[path.Count - 1].parent.X, path[path.Count - 1].parent.Y]);
                        }

                        path.Reverse();
                    }
                    else
                    {
                        if (map[p.X, p.Y].open == true)
                        {
                            if (map[pos.X, pos.Y].G + 10 < map[p.X, p.Y].G)
                            {

                            }
                        }
                        else
                        {
                            map[p.X, p.Y].open = true;
                            map[p.X, p.Y].parent = pos;
                            map[p.X, p.Y].H = GetH(p, dest);
                            map[p.X, p.Y].G = map[pos.X, pos.Y].G + 10;
                            map[p.X, p.Y].F = map[p.X, p.Y].G + map[p.X, p.Y].H;

                            WestLeft = (map[p.X + 1, p.Y].walkable == true) ? true : false;

                            if (p.X - 1 >= 0)
                                WestRight = (map[p.X - 1, p.Y].walkable == true) ? true : false;
                            else
                                WestRight = false;

                            openList.Add(map[p.X, p.Y]);
                        }
                    }
                }
                else
                {

                    WestLeft = false;
                    WestRight = false;
                }

                //values += WestLeft.ToString() + " " + WestRight.ToString() + "\r\n";
            }
        }
    }
}

1 个答案:

答案 0 :(得分:0)

当怪物移动时,没有必要使用探路者。它找到了一条路径,使用那条路径。

如果计算出的路径被无法通过的障碍物阻挡,则必须重新计算路径

然后你的zobie必须停下来。无论如何,路径无效,它无法移动。

如果环境发生变化,表明会有更便宜的路径,则可以重新计算路径

如果全新路径比当前路径的剩余部分便宜,则仅使用新路径。

编辑:

优化探路者:

openList = openList.OrderBy(p => p.F).ToList();

这一行每次都会对列表进行排序。您需要一个排序列表,在插入后对其进行排序。作为快速修复,在插入内容后调用openList.Sort()。这可能不是最佳选择,因为它会对整个列表进行排序,而您已经知道只有一个元素是未排序的。

在.NET中查找优先级队列类的同样问题是here

您的四个北/东/南/西功能是代码重复。检查它们的共同点,并将其提取到一个共同的功能中。您的代码将只是现在的25%。这并不快,但代码越少,就越容易看到内容。