游戏AI中的寻路算法(A*算法)

——个人笔记

本人学习《Unity人工智能游戏开发》和Siki学院的《Unity游戏开发人工智能编程》的笔记

  • 介绍

    • A*寻路算法并不是找到最优路径,只是找到相对近的路径,因为找最优要把所有可行路径都找出来进行对比,消耗性能太大,寻路效果只要相对近路径就行了。不过现在3D游戏很多都用unity中的自动寻路系统。
  • 寻路步骤

    1. 从起点开始, 把它作为待处理的方格存入一个"开启列表", 即把起点放入“开启列表”,开启列表就是一个等待检查方格的列表。
    2. 寻找开启列表中F值最小的点C(一开始只有起点)周围可以到达的方格(可到达的意思是其不是障碍物,也不存在关闭列表中的方格),若是上下左右有障碍物,那么也不能向那个方向的斜方向走。
    3. 计算C周围可到达的方格的F值。将还没在开启列表的放入"开启列表", 并设置它们的"父方格"为点C。 如果C中某个可到达的方格已经在开启列表那么如果F值更优才要更改F值和把其"父方格"设为点C。
    4. 把2中的点C从"开启列表"中删除并存入"关闭列表", "关闭列表"中存放的都是不需要再次检查的方格。
    5. 如果2中点C不是终点并且开启列表的数量大于零,那么继续从第2步开始。如果是终点开始返回,如果开启列表数量为零,那么就是找不到有效路径。
  • F值(找当前有可能最优点的凭据)

    • F = G + H (F是预计到终点的总移动消耗)
      G 表示从起点 A 移动到网格上指定方格的移动耗费 (可沿斜方向移动)。
      H 表示从指定的方格移动到终点 B 的预计耗费 (H 有很多计算方法, 这里我们设定只可以上下左右移动,即该点与终点的X,Y坐标(指的是平面的x、y值)各差值的和)。

  • 简略图示

  • 图中,绿色方格为起点,红色方格为终点,蓝色为障碍物。 第一次就会找到四周的9个方格并放入开启列表,并计算他们的F值。图的计算方式和步骤一样只是单位长度和代码中的不同。找最优F值就是C点,接着找C周围的方格,把C移除开启列表,加入关闭列表,接着找到现在最优F值是D点,按照步骤走就行,这里不详细说了。

游戏AI中的寻路算法(A*算法)_第1张图片

  • 这里是遍历到终点的图示,蓝色边是加入关闭列表的,绿色边是加入开启列表的。
    游戏AI中的寻路算法(A*算法)_第2张图片

  • 最后是找回可行路径

游戏AI中的寻路算法(A*算法)_第3张图片

  • 方格类(Point)

    • 这个是方格属性类,即是地图的方格抽象化。代码:
public class Point 
{
    public Point Parent { get; set; }

    public int X { get; set; }
    public int Y { get; set; }
    
    public float G { get; set; }//起点到该点的距离
    public float H { get; set; }//预计该点到终点距离
    public float F { get; set; }//最终距离F=G+H

    public bool IsWall { get; set; }
	//更新父方格和F值的方法
    public void UpdateParent(Point parent, float g)
    {
        this.Parent = parent;
        this.G = g;
        F = G + H;
    }

    public Point(int x, int y)
    {
        this.X = x;
        this.Y = y;
        IsWall = false;
        this.Parent = null;        
    }    
}
  • 初始化地图

    • 这里只是记录A*算法最核心的东西,所以地图初始化这里就直接自己手动标,没有做成可视图的效果,其实就是初始化一个point的二维数组,代码:
 private void InitMap()
    {
        for (int x = 0; x < mapWeight; x++)
        {
            for (int y = 0; y < mapHeight; y++)
            {
                map[x, y] = new Point(x, y);
            }
        }
        map[4, 2].IsWall = true;
        map[4, 3].IsWall = true;
        map[4, 4].IsWall = true;
    }
  • 核心的思路

    • 按照步骤,写出主体代码:
private void FindPath(Point start, Point end)
    {
    	//开启列表
        List openList = new List();
        //关闭列表
        List closeList = new List();
        openList.Add(start);
        while (openList.Count > 0)
        {
        	//找开启列表中F值最小的方格
            Point point = FindMinFofPoint(openList);
            openList.Remove(point);
            closeList.Add(point);
            //获取C点周围的方格(不含障碍物)
            List surroundList = GetSurroundPoints(point);
            //把周围方格中已存在关闭列表的除去
            PointsFilter(surroundList, closeList);
          	//计算他们的F值和设置父亲
            foreach (Point surroundPoint in surroundList)
            {
                if (openList.IndexOf(surroundPoint) > -1)
                {
                	//一个点的H值不变
                	//所以已存在列表中的方格,只要算G值更优就是F值更优
                    float nowG = Vector2.Distance(new Vector2(surroundPoint.X, surroundPoint.Y), new Vector2(point.X, point.Y)) + point.G;
                    if (nowG < surroundPoint.G)
                    {
                    	//更新父方格和F值的方法
                        surroundPoint.UpdateParent(point, nowG);
                    }
                }
                else
                { 
                    surroundPoint.Parent = point;
                     //计算F值
                    CalF(surroundPoint, end);
                    openList.Add(surroundPoint);
                }
            }
            //判断一下是否是终点
            if (openList.IndexOf(end) > -1)
            {
                break;
            }
        }
    }
  • 找开启列表F值最小的方法

//找在openlist中F值最小的
    private Point FindMinFofPoint(List openList)
    {
        float f = float.MaxValue;
        Point temp = null;
        foreach (Point p in openList)
        {
            if (p.F < f)
            {
                temp = p;
                f = p.F;
            }
        }
        return temp;
    }
  • 获取周围有效方格


    //获取周围所有走的格子
    private List GetSurroundPoints(Point point)
    {
        List list = new List();
        Point up = null, down = null, right = null, left = null;
        if (point.Y > 0)
        {
            down = map[point.X, point.Y - 1];
            if (!down.IsWall)
            {
                list.Add(down);
            }
        }
        if (point.Y < mapHeight - 1)
        {
            up = map[point.X, point.Y + 1];
            if (!up.IsWall)
            {
                list.Add(up);
            }
        }
        if (point.X > 0)
        {
            left = map[point.X - 1, point.Y];
            if (!left.IsWall)
            {
                list.Add(left);

                if (up != null && up.IsWall == false&& !map[point.X - 1, point.Y + 1].IsWall)
                    list.Add(map[point.X - 1, point.Y + 1]);//lu          
                if (down != null && !down.IsWall&& !map[point.X - 1, point.Y - 1].IsWall)
                    list.Add(map[point.X - 1, point.Y - 1]);//ld
            }

        }
        if (point.X < mapWeight - 1)
        {
            right = map[point.X + 1, point.Y];
            if (!right.IsWall)
            {
                list.Add(right);

                if (up != null && !up.IsWall&&!map[point.X + 1, point.Y + 1].IsWall)
                    list.Add(map[point.X + 1, point.Y + 1]);//ru
                if (down != null && !down.IsWall&&!map[point.X + 1, point.Y - 1].IsWall)
                    list.Add(map[point.X + 1, point.Y - 1]);//rd
            }
        }
        return list;
    }
    
	//除去周围可行格子中已经存在closelist的格子
    private void PointsFilter(List src, List closeList)
    {
        foreach (Point p in closeList)
        {
            if (src.IndexOf(p) > -1)
            {
               
                src.Remove(p);
            }
        }
    }
  • 计算F值

//计算F,G,H值
    private void CalF(Point now, Point end)
    {
        float h = Mathf.Abs(now.X - end.X) + Mathf.Abs(now.Y - end.Y);
        float g = 0;
        if (now.Parent != null)
        {
            g = Vector2.Distance(new Vector2(now.X, now.Y), new Vector2(now.Parent.X, now.Parent.Y))+now.Parent.G;
        }
        float f = h + g;

        now.H = h;
        now.G = g;
        now.F = f;
    }

你可能感兴趣的:(Untiy)