A*寻路算法

2018-08-24  本文已影响15人  BugUnknown
寻路效果.gif

代码实现

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

/// <summary>
/// 格子类型
/// </summary>
public enum ItemType
{
    Normal,
    Obsticle,
    Start,
    End
}

public class AStar : MonoBehaviour
{
    /// <summary>
    /// Item预设体
    /// </summary>
    public GameObject itemPrefab;
    //起点终点坐标
    public int startPointX = 1;
    public int startPointY = 2;
    public int endPointX = 10;
    public int endPointY = 12;
    //自定义偏移量
    public float offset = 0.5f;
    //障碍物出现的概率
    public int obsticleScale = 60;
    //地图格子的长宽
    private int mapX;
    private int mapY;
    //地图偏移量
    private float mapOffsetX;
    private float mapOffsetY;
    //格子数组
    private Item[,] items;
    /// <summary>
    /// 开启列表(将所有计算过FGH值的格子存储)
    /// </summary>
    private List<Item> openList;
    /// <summary>
    /// 关闭列表(将被作为中心计算过的格子)
    /// </summary>
    private List<Item> closeList;
    /// <summary>
    /// 结果路径
    /// </summary>
    private Stack<Item> path;

    void Awake()
    {
        openList = new List<Item>();
        closeList = new List<Item>();
        path = new Stack<Item>();
    }

    void Start()
    {
        MapReady();
        AStarPathFinding();
    }

    /// <summary>
    /// 地图准备
    /// </summary>
    void MapReady()
    {
        //计算地图长宽
        mapX = (int)(transform.localScale.x * 20);
        mapY = (int)(transform.localScale.z * 20);
        //实例数组
        items = new Item[mapX, mapY];
        //计算地图偏移量
        mapOffsetX = -transform.localScale.x * 10 / 2;
        mapOffsetY = -transform.localScale.z * 10 / 2;
        //生成格子
        for (int i = 0; i < mapX; i++)
        {
            for (int j = 0; j < mapY; j++)
            {
                //获取当前格子
                GameObject crtItem = Instantiate(itemPrefab,
                    new Vector3(i / 2f, 0, j / 2f) +
                    new Vector3(mapOffsetX + offset, 0,
                        mapOffsetY + offset),
                    Quaternion.identity);
                //填充数组
                items[i, j] = crtItem.GetComponent<Item>();
                //设置坐标
                items[i, j].SetItemXY(i, j);
                //随机
                int r = Random.Range(1, 101);
                if (r <= obsticleScale)
                {
                    //设置障碍物
                    items[i, j].SetItemType(ItemType.Obsticle);
                }
            }
        }
        //设置起点终点
        items[startPointX, startPointY].SetItemType(ItemType.Start);
        items[endPointX, endPointY].SetItemType(ItemType.End);
    }

    /// <summary>
    /// A*寻路
    /// </summary>
    void AStarPathFinding()
    {
        //起点格子作为当前格子
        Item currentItem = null;
        //添加起点格子到开启列表
        openList.Add(items[startPointX, startPointY]);
        //循环遍历
        while (openList.Count > 0)
        {
            //获取F值最小的格子作为当前格子
            currentItem = openList[0];
            //如果发现了终点
            if (currentItem.GetItemType()
               == ItemType.End)
            {
                //TODO:整理结果路径
                GeneratePath(currentItem);
                return;
            }
            //发现周围的格子
            for (int i = -1; i <= 1; i++)
            {
                for (int j = -1; j <= 1; j++)
                {
                    //越过当前格子
                    if (i == 0 && j == 0)
                        continue;
                    //被发现的格子坐标
                    int p = currentItem.GetItemX() + i;
                    int q = currentItem.GetItemY() + j;
                    if (p < 0 || q < 0 || p >= mapX
                       || q >= mapY ||
                       items[p, q].GetItemType() == ItemType.Obsticle ||
                        closeList.Contains(items[p, q]))
                    {
                        continue;
                    }
                    //计算FGH
                    int g = currentItem.g + (int)(Mathf.Sqrt(i * i + j * j) * 10);
                    //如果该格子的g未被计算,或值太大,覆盖它
                    if (items[p, q].g == 0 || items[p, q].g > g)
                    {
                        items[p, q].g = g;
                        //设置发现者
                        items[p, q].parent = currentItem;
                    }
                    //曼哈顿计算h值
                    items[p, q].h = (Mathf.Abs(p - endPointX)
                        + Mathf.Abs(q - endPointY)) * 10;
                    //计算F
                    items[p, q].f = items[p, q].g + items[p, q].h;
                    //添加到开启列表
                    openList.Add(items[p, q]);
                }
            }
            //For循环结束
            //移除当前格子从开启列表
            openList.Remove(currentItem);
            //放置到关闭列表
            closeList.Add(currentItem);
            //判断
            if (openList.Count == 0)
                Debug.Log("无法到达目标点!");
            //排序
            openList.Sort();
        }
    }

    /// <summary>
    /// 生成路径
    /// </summary>
    void GeneratePath(Item item)
    {
        if (item.parent != null)
        {
            //压栈
            path.Push(item.parent);
            //递归寻址
            GeneratePath(item.parent);
        }
        else
        {
            //输出结果
            StartCoroutine(ShowPath());
        }
    }

    /// <summary>
    /// 展示结果
    /// </summary>
    /// <param name="item">Item.</param>
    IEnumerator ShowPath()
    {
        while (path.Count > 0)
        {
            //出栈
            Item item = path.Pop();
            if (item.GetItemType() != ItemType.Start)
            {
                //标记
                item.SetColor(Color.black);
            }
            yield return new WaitForSeconds(0.2f);
        }
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            UnityEngine.SceneManagement.SceneManager.LoadScene(0);
        }
    }
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Item : MonoBehaviour, System.IComparable
{

    //格子类型
    private ItemType itemType;
    //格子坐标
    private int x;
    private int y;
    //寻路估量代价值
    public int f;
    public int g;
    public int h;
    //当前格子的发现者
    public Item parent;

    private MeshRenderer meshRenderer;

    void Awake()
    {
        meshRenderer = GetComponent<MeshRenderer>();
    }
    /// <summary>
    /// 设置格子类型
    /// </summary>
    /// <param name="t">T.</param>
    public void SetItemType(ItemType t)
    {
        //设置
        itemType = t;
        switch (t)
        {
            case ItemType.Start:
                meshRenderer.material.color = Color.red;
                break;
            case ItemType.End:
                meshRenderer.material.color = Color.green;
                break;
            case ItemType.Obsticle:
                meshRenderer.material.color = Color.blue;
                break;
            default:
                meshRenderer.material.color = Color.white;
                break;
        }
    }

    /// <summary>
    /// 获取格子类型
    /// </summary>
    /// <returns>The item type.</returns>
    public ItemType GetItemType()
    {
        return itemType;
    }

    public int GetItemX()
    {
        return x;
    }

    public int GetItemY()
    {
        return y;
    }

    /// <summary>
    /// 设置坐标
    /// </summary>
    /// <param name="x">The x coordinate.</param>
    /// <param name="y">The y coordinate.</param>
    public void SetItemXY(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
    /// <summary>
    /// 上色
    /// </summary>
    /// <param name="col">Col.</param>
    public void SetColor(Color col)
    {
        meshRenderer.material.color = col;
    }

    /// <summary>
    /// 排序规则
    /// </summary>
    /// <returns>The to.</returns>
    /// <param name="obj">Object.</param>
    public int CompareTo(object obj)
    {
        //新的Item
        Item newItem = obj as Item;
        if (newItem.f > this.f)
        {
            return -1;
        }
        else if (newItem.f < this.f)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读