unity3d A* 寻路生成错误路径

pkwftd7m  于 2023-03-23  发布在  其他
关注(0)|答案(2)|浏览(130)

我在做一个迷宫游戏,如果玩家进入敌人的射程,敌人就会准备追击玩家,我用A* 寻路算法来做,我用程序化的方式来构建迷宫,迷宫中的每个方块都有一个带x和z位置的MapLocation,所以起始位置是敌人的位置,目标是玩家的位置,但有时候,当玩家进入敌人的射程,路径选择忽略了迷宫中的一些块来到达玩家,我上传了一个视频来说明这个问题:
https://www.youtube.com/watch?v=_amIf75wruw

IEnumerator Search(Vector3 start, Vector3 goal)
        {
            pathSuccess = false;
            waypoints = new Vector3[0];
            Heap<Path> open = new Heap<Path>(maze.MaxSize);
            HashSet<Path> closed = new HashSet<Path>();

            Path startNode = new Path(maze.GetMapLocation(start), 0, 0, 0, null);
            Path goalNode = new Path(maze.GetMapLocation(goal), 0, 0, 0, null);

            open.Add(startNode);
            lastPos = startNode;

            while (open.Count > 0)
            {
                if (lastPos.Equals(goalNode))
                {
                    pathSuccess = true;
                    break;
                }
                foreach (MapLocation dir in maze.directions)
                {
                    MapLocation neighbour = dir + lastPos.location;
                    if (maze.map[neighbour.x, neighbour.z] == 1) continue;
                    if (neighbour.x < 1 || neighbour.x >= maze.width || neighbour.z < 1 || neighbour.z >= maze.depth) continue;
                    if (IsClosed(neighbour, closed)) continue;

                    float G = Vector2.Distance(lastPos.location.ToVector(), neighbour.ToVector()) + lastPos.G;
                    float H = Vector2.Distance(neighbour.ToVector(), goalNode.location.ToVector());
                    float F = G + H;

                    if (!UpdateMarker(neighbour, G, H, F, lastPos, open))
                        open.Add(new Path(neighbour, G, H, F, lastPos));
                }
                Path pm = open.RemoveFirst();
                closed.Add(pm);

                lastPos = pm;
            }
            yield return null;
            if (pathSuccess)
            {
                waypoints = RetracePath(startNode);
            }
            PathRequestManager.Instance.FinishedProcessingPath(waypoints, pathSuccess);
        }

        bool UpdateMarker(MapLocation pos, float g, float h, float f, Path prt, Heap<Path> open)
        {
            foreach (Path p in open)
            {
                if (p.location.Equals(pos))
                {
                    p.G = g;
                    p.H = h;
                    p.F = f;
                    p.parent = prt;
                    return true;
                }
            }
            return false;
        }

        bool IsClosed(MapLocation marker, HashSet<Path> closed)
        {
            foreach (Path p in closed)
            {
                if (p.location.Equals(marker)) return true;
            }
            return false;
        }

        Vector3[] RetracePath(Path startNode)
        {
            Path begin = lastPos;
            List<Vector3> path = new List<Vector3>();
            while (!startNode.Equals(begin) && begin != null)
            {
                path.Add(begin.Position(maze));
                begin = begin.parent;
            }
            path.Reverse();
            return path.ToArray();
        }

public MapLocation GetMapLocation(Vector3 position)
        {
            MapLocation mapLocation = new MapLocation((int)position.x / scale, (int)position.z / scale);
            return mapLocation;
        }
public List<MapLocation> directions = new List<MapLocation>() {
                                            new MapLocation(1,0),
                                            new MapLocation(0,1),
                                            new MapLocation(-1,0),
                                            new MapLocation(0,-1) };

迷宫是这样生成的:

public class Recursive : Maze
    {
        public override void Generate()
        {
            Generate(5, 5);
        }

        void Generate(int x, int z)
        {
            if (CountSquareNeighbours(x, z) >= 2) return;
            map[x, z] = 0;

            directions.Shuffle();

            Generate(x + directions[0].x, z + directions[0].z);
            Generate(x + directions[1].x, z + directions[1].z);
            Generate(x + directions[2].x, z + directions[2].z);
            Generate(x + directions[3].x, z + directions[3].z);
        }

    }


public byte[,] map;
 public virtual void Generate()
        {
            for (int z = 0; z < depth; z++)
                for (int x = 0; x < width; x++)
                {
                   if(Random.Range(0,100) < 50)
                     map[x, z] = 0;     //1 = wall  0 = corridor
                }
        }

我已经试过使用曼哈顿的距离,它没有任何区别:

float H = Mathf.Abs(neighbour.x - goalNode.location.x) + Mathf.Abs(neighbour.z - goalNode.location.z);
flvlnr44

flvlnr441#

调试当你在GetMapLocation()中按比例划分时得到的值,它可能会因为缩放/舍入而跳过单元格?
我会有一个map[x,y]数组,然后按比例放大,以使其在屏幕上的像素正确,但在原始未缩放的Map[x,y]数组上进行寻路。IE将逻辑与渲染分开。

e4yzc0pl

e4yzc0pl2#

你的A* 寻路算法的问题与你的迷宫的生成有关。由于你是按程序生成的,有时寻路算法会跳过迷宫的一些区块到达玩家。
可以通过调整迷宫的生成来解决此问题,以确保始终可以从迷宫中的任何其他点到达任何点。
您可以使用迷宫生成算法,例如prim或kruskal算法。
还有一件事我注意到的是,你正在使用的启发式函数来计算从节点到目标的距离,对于迷宫尝试使用欧几里德或切比雪夫距离来说并不是闲置的。
希望能有所帮助

相关问题