U3d_人工智能[一] 之A*寻路算法

1. 主要思想:

 首先,寻路即使寻点,一个一个正确的点走过去,便达成寻路的要求.
 这里,每个点都有3个属性:F,G,H.H是当前点到达终点的预计消耗,可以用距离表示,但是这里我们的计算方法是
/*
*nowend都是点,一个是当前点,一个是终点
*因此,这里计算出来的距离就是两点的x差和y差的和
*/
H = Mathf.Abs(now.x - end.x) + Mathf.Abs(now.y - end.y);
而G表示的是起点到达当前点的消耗,可以用起点到当前点的距离来表示.但是计算的时候是一个点一个点的加过来,所以G的值在计算过程中可能会改变,因为父节点有可能会改变,我们在算法中是这样计算G的值的:
//now.Parent就是获取到当前点的父节点
G=Vector2.Distance(new Vector2(now.x,now.y),new Vector2(now.Parent.x,now.Parent.y))+now.Parent.G;
最后的F=G+H;

2. 创建Point类

     此类表示点,也就之最基本的点,寻路时候的一个个的点.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Point{

    public Point Parent { get; set; }
    public float F { get; set; }
    public float G { get; set; }
    public float H { get; set; }
    public int x { get; set; }
    public int y { get; set; }
    public bool isWall { get; set; }
    public Point(int x, int y, Point parent=null)
    {
        this.x = x;
        this.y = y;
        Parent = parent;
        isWall = false;
    }
    /// <summary>
    /// 更新父节点、g和f、值
    /// </summary>
    /// <param name="parent"></param>
    /// <param name="g"></param>
    public void UpdateParent(Point parent, float g)
    {
        Parent = parent;
        G = g;
        F = G + H;
    }
}

3. 创建Astar类实现寻路

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AStar : MonoBehaviour
{

    // Use this for initialization
    void Start()
    {
        InitMap();
        Point start = map[2, 3];
        Point end = map[6, 3];
        FindPath(start, end);
        ShowPath(start, end);
    }
    private const int mapWith = 15;
    private const int mapHeigh = 15;
    private Point[,] map = new Point[mapWith, mapHeigh];

    /// <summary>
    /// 构造地图
    /// </summary>
    public void InitMap()
    {

        for (int x = 0; x < mapWith; x++)
        {
            for (int y = 0; y < mapHeigh; y++)
            {
                map[x, y] = new Point(x, y);
            }
        }
        map[4, 2].isWall = true;
        map[4, 3].isWall = true;
        map[4, 4].isWall = true;
    }
    /// <summary>
    /// 核心寻路
    /// </summary>
    /// <param name="star">开始点</param>
    /// <param name="end">结束点</param>
    public void FindPath(Point star, Point end)
    {

        List<Point> openList = new List<Point>();
        List<Point> closeList = new List<Point>();
        openList.Add(star);
        while (openList.Count > 0)
        {

            Point point = FindMinPointF(openList);
            openList.Remove(point);
            closeList.Add(point);
            List<Point> surrounPoints = GetSurroundPoints(point);

            PointSFilter(surrounPoints, closeList);
            //遍历获取到的所有点,并做相关处理

            foreach (Point surroundPoint in surrounPoints)
            {
                if (openList.IndexOf(surroundPoint) > -1)
                {//如果该点已经在开始列表上,再来看是否要更新g值

                    float nowG = CalcG(surroundPoint, point);

                    if (nowG < surroundPoint.G)
                    { //如果新的g值小于原来的g值,就更新g值

                        surroundPoint.UpdateParent(point, nowG);
                    }
                }
                else
                {//如果该店不再开始列表上,就添加该点到开始列表上,并做相关构造处理

                    surroundPoint.Parent = point;
                    CalcF(surroundPoint, end);

                    openList.Add(surroundPoint);

                }
            }

            if (openList.IndexOf(end) > -1)
            {//如果开启列表里面出现了终点,就结束循环

                break;
            }
        }
    }
    /// <summary>
    /// 计算F值
    /// </summary>
    /// <param name="now">当前点</param>
    /// <param name="end">终点</param>
    private void CalcF(Point now, Point end)
    {

        now.H = Mathf.Abs(now.x - end.x) + Mathf.Abs(now.y - end.y);
        if (now.Parent != null)
        {
            now.G = Vector2.Distance(new Vector2(now.x, now.y), new Vector2(now.Parent.x, now.Parent.y)) + now.Parent.G;
        }
        now.F = now.H + now.G;
    }
    /// <summary>
    /// 寻找开启列表里面F值最小的点
    /// </summary>
    /// <param name="openList"></param>
    /// <returns></returns>
    private Point FindMinPointF(List<Point> openList)
    {

        float f = float.MaxValue;
        Point temp = null;
        foreach (Point point in openList)
        {
            if (point.F < f)
            {
                f = point.F;
                temp = point;
            }
        }
        return temp;
    }
    /// <summary>
    /// 获取point点周围的点
    /// </summary>
    /// <param name="point"></param>
    /// <returns></returns>
    private List<Point> GetSurroundPoints(Point point)
    {

        Point up = null, down = null, left = null, right = null;
        Point lu = null, ru = null, ld = null, rd = null;
        if (point.y < mapHeigh - 1)
        {
            up = map[point.x, point.y + 1];
        }
        if (point.y > 0)
        {
            down = map[point.x, point.y - 1];
        }
        if (point.x > 0)
        {
            left = map[point.x - 1, point.y];
        }
        if (point.x < mapWith - 1)
        {
            right = map[point.x + 1, point.y];
        }


        if (up != null && left != null)
        {
            lu = map[point.x - 1, point.y + 1];
        }
        if (up != null && right != null)
        {
            ru = map[point.x + 1, point.y + 1];
        }
        if (down != null && left != null)
        {
            ld = map[point.x - 1, point.y - 1];
        }
        if (down != null && right != null)
        {
            rd = map[point.x + 1, point.y - 1];
        }
        List<Point> list = new List<Point>();
        if (down != null && down.isWall == false)
        {
            list.Add(down);
        }
        if (up != null && up.isWall == false)
        {
            list.Add(up);
        }
        if (left != null && left.isWall == false)
        {
            list.Add(left);
        }
        if (right != null && right.isWall == false)
        {
            list.Add(right);
        }
        if (lu != null && lu.isWall == false && left.isWall == false && up.isWall == false)
        {
            list.Add(lu);
        }
        if (ld != null && ld.isWall == false && left.isWall == false && down.isWall == false)
        {
            list.Add(ld);
        }
        if (ru != null && ru.isWall == false && right.isWall == false && up.isWall == false)
        {
            list.Add(ru);
        }
        if (rd != null && rd.isWall == false && right.isWall == false && down.isWall == false)
        {
            list.Add(rd);
        }
        return list;
    }
    /// <summary>
    /// 过滤掉关闭列表上的点
    /// </summary>
    /// <param name="now"></param>
    /// <param name="close"></param>
    private void PointSFilter(List<Point> now, List<Point> close)
    {

        foreach (Point point in close)
        {
            if (now.IndexOf(point) > -1)
            {
                now.Remove(point);
            }
        }
    }
    /// <summary>
    /// 计算now点的G值并返回
    /// </summary>
    /// <param name="now"></param>
    /// <param name="parent"></param>
    /// <returns></returns>
    private float CalcG(Point now, Point parent)
    {

        return Vector2.Distance(new Vector2(now.x, now.y), new Vector2(parent.x, parent.y)) + parent.G;
    }
    /// <summary>
    /// 显示路径
    /// </summary>
    /// <param name="star"></param>
    /// <param name="end"></param>
    public void ShowPath(Point star, Point end)
    {

        Point temp = end;

        //画路径
        while (true)
        {
            Color c = Color.gray;
            if (temp == star)
            {
                c = Color.green;
            }
            else if (temp == end)
            {
                c = Color.red;
            }
            CreateCube(temp.x, temp.y, c);
            if (temp.Parent == null)
                break;
            temp = temp.Parent;
        }
        //画障碍物
        for (int x = 0; x < mapWith; x++)
        {
            for (int y = 0; y < mapHeigh; y++)
            {
                if (map[x, y].isWall)
                {
                    CreateCube(x, y, Color.blue);
                }
            }
        }
    }
    /// <summary>
    /// 选择地点创建正方体
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <param name="color">正方体颜色</param>
    private void CreateCube(int x, int y, Color color)
    {
        print("CreateCube");
        GameObject go = GameObject.CreatePrimitive(PrimitiveType.Cube);//创建几何体
        go.transform.position = new Vector3(x, y, 0);
        go.GetComponent<Renderer>().material.color = color;
    }
}
这里较为重要是核心算法,我也就重点拿出来看一下:
首先是创建了两个列表openList和closeList,就是用来存放需要检查的点,和不需要检查的点,比如出发点,一开始就加入到closeList关闭列表,然后通过FindMinPointF()获取到其周围可以走的点,经过筛选再加入到开启列表等待检查.
遍历获取到的点进行检,如果该点已经在开始列表上,再来看是否要更新g值,如果新的g值小于原来的g值,就更新g值,如果该点不再开始列表上,就添加该点到开始列表上,并做相关构造处理.
如此反复添加新的点进入开启列表,范围就会慢慢扩大,并且每个点都会有它的父节点,当终点进入开启列表时就结束循环,同样的终点也有父节点,父节点也有父节点,直到父节点为空,也就是起点,这样就出现了一条由终点指向起点的链表,根据这个方法就获取到了最短路径.
    /// <summary>
    /// 核心寻路
    /// </summary>
    /// <param name="star">开始点</param>
    /// <param name="end">结束点</param>
    public void FindPath(Point star, Point end)
    {
        List<Point> openList = new List<Point>();
        List<Point> closeList = new List<Point>();
        openList.Add(star);
        while (openList.Count > 0)
        {

            Point point = FindMinPointF(openList);
            openList.Remove(point);
            closeList.Add(point);
            List<Point> surrounPoints = GetSurroundPoints(point);

            PointSFilter(surrounPoints, closeList);
            //遍历获取到的所有点,并做相关处理

            foreach (Point surroundPoint in surrounPoints)
            {
                if (openList.IndexOf(surroundPoint) > -1)
                {//如果该点已经在开始列表上,再来看是否要更新g值

                    float nowG = CalcG(surroundPoint, point);

                    if (nowG < surroundPoint.G)
                    { //如果新的g值小于原来的g值,就更新g值

                        surroundPoint.UpdateParent(point, nowG);
                    }
                }
                else
                {//如果该店不再开始列表上,就添加该点到开始列表上,并做相关构造处理

                    surroundPoint.Parent = point;
                    CalcF(surroundPoint, end);

                    openList.Add(surroundPoint);

                }
            }

            if (openList.IndexOf(end) > -1)
            {//如果开启列表里面出现了终点,就结束循环

                break;
            }
        }
    }
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值