U3d_人工智慧[一] 之A*尋路演算法_人工智慧

來源:互聯網
上載者:User
1. 主要思想:
 首先,尋路即使尋點,一個一個正確的點走過去,便達成尋路的要求. 這裡,每個點都有3個屬性:F,G,H.H是當前點到達終點的預計消耗,可以用距離表示,但是這裡我們的計算方法是
/**now和end都是點,一個是當前點,一個是終點*因此,這裡計算出來的距離就是兩點的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;            }        }    }

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.