淘先锋技术网

首页 1 2 3 4 5 6 7

有段时间没用到了,最近看的项目中涉及到寻路,就花点时间自己写了一个unity A*寻路的demo

GameStart在场景任找一个游戏物体挂载,代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;

public class GameStart : MonoBehaviour
{
    public Transform m_parent;
    public GameObject m_item;
    int _width = 35;
    // Start is called before the first frame update
    void Start()
    {
        for(int i = 0; i < AStarManager.maxRow; i++)
        {
            for(int j = 0; j < AStarManager.maxCol; j++)
            {
                GameObject obj = Instantiate(m_item,m_parent);
                obj.name = string.Format("{0}-{1}",i,j);
                obj.transform.localPosition = new Vector3(-640 + _width + _width * i, -320 + _width + _width * j, 0);
                WayItem _way = obj.AddComponent<WayItem>();
                AStarManager.Inst.AddItem(i,j,_way);
            }
        }
    }

    private void OnGUI()
    {
        GUILayout.BeginHorizontal("box");
        if (GUILayout.Button("Start",GUILayout.Width(100))) {
            AStarManager.Inst.m_ClickType = AStarManager.ClickType.StartPos;
        }
        if (GUILayout.Button("End", GUILayout.Width(100)))
        {
            AStarManager.Inst.m_ClickType = AStarManager.ClickType.EndPos;
        }
        if (GUILayout.Button("Obstacle", GUILayout.Width(100)))
        {
            AStarManager.Inst.m_ClickType = AStarManager.ClickType.Obstacle;
        }
        if (GUILayout.Button("Find", GUILayout.Width(100)))
        {
            AStarManager.Inst.m_ClickType = AStarManager.ClickType.None;
            AStarManager.Inst.StartFind();
        }
        if (GUILayout.Button("Clear", GUILayout.Width(100)))
        {
            AStarManager.Inst.m_ClickType = AStarManager.ClickType.None;
            AStarManager.Inst.Reset();
        }
        GUILayout.EndHorizontal();
    }
}

AStarManager是逻辑代码,代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class WayItem : MonoBehaviour{
    public bool isObstacle = false;
    public int index_x = 0;
    public int index_y = 0;
    public float dis_f = 0;
    public float dis_g = 0;
    public float dis_h = 0;
    public WayItem lastWay = null;

    Image m_img = null;
    void Start()
    {
        m_img = gameObject.GetComponent<Image>();

        EventTrigger.Entry _entry = new EventTrigger.Entry();
        _entry.eventID = EventTriggerType.PointerClick;
        _entry.callback.AddListener(OnClick);
        EventTrigger _event = gameObject.AddComponent<EventTrigger>();
        _event.triggers.Add(_entry);
    }

    void OnClick(BaseEventData _data) {
        //Debug.Log(string.Format("Click name:{0},type:{1}",gameObject.name,AStarManager.Inst.m_ClickType));
        if (AStarManager.Inst.m_ClickType == AStarManager.ClickType.StartPos)
        {
            SetColor(m_img.color == Color.white ? Color.black : Color.white);
            AStarManager.Inst.SetStartPos(this);
        }
        else if (AStarManager.Inst.m_ClickType == AStarManager.ClickType.EndPos)
        {
            SetColor(m_img.color == Color.white ? Color.gray : Color.white);
            AStarManager.Inst.SetEndPos(this);
        }
        else if (AStarManager.Inst.m_ClickType == AStarManager.ClickType.Obstacle) {
            isObstacle = m_img.color == Color.white;
            SetColor(isObstacle ? Color.red : Color.white);
        }
    }

    public void SetColor(Color _color)
    {
        m_img.color = _color;
    }
}

public class AStarManager
{
    private static AStarManager _inst = null;
    public static AStarManager Inst {
        get {
            if (_inst == null) _inst = new AStarManager();
            return _inst;
        }
    }

    public enum ClickType { 
        None,StartPos,EndPos,Obstacle
    }
    public ClickType m_ClickType = ClickType.None;
    public WayItem m_start = null;
    public WayItem m_end = null;

    public const int maxRow = 10;
    public const int maxCol = 5;

    public void Init() { 

    }

    WayItem[,] m_items = new WayItem[maxRow, maxCol];
    public void AddItem(int _x, int _y,WayItem _item) {
        _item.index_x = _x;
        _item.index_y = _y;
        m_items[_x, _y] = _item;
    }

    public void Reset() {
        m_start = null;
        m_end = null;
        for (int i = 0; i < maxRow; i++)
        {
            for (int j = 0; j < maxCol; j++)
            {
                m_items[i, j].isObstacle = false;
                m_items[i, j].SetColor(Color.white);
            }
        }
    }

    public void SetStartPos(WayItem _item) {
        if (m_start != null) {
            m_start.SetColor(Color.white);
        }
        m_start = _item;
    }

    public void SetEndPos(WayItem _item)
    {
        if (m_end != null)
        {
            m_end.SetColor(Color.white);
        }
        m_end = _item;
    }

    public void StartFind() {
        FindPath(m_start,m_end);
    }

    WayItem GetItem(int _x,int _y) {
        if(_x>=0 && _x<maxRow && _y>=0 && _y < maxCol)
        {
            return m_items[_x, _y];
        }
        return null;
    }

    List<WayItem> GetNearItems(WayItem _item) {
        List<WayItem> _res = new List<WayItem>();
        for (int i = -1; i <= 1; i++) {
            for (int j = -1; j <= 1; j++) {
                if ((i == 0 || j == 0) && (i != 0 || j != 0)) {
                    int _x = _item.index_x + i;
                    int _y = _item.index_y + j;
                    WayItem _tmp = GetItem(_x,_y);
                    if (_tmp != null) {
                        _res.Add(_tmp);
                    }
                }
            }
        }
        return _res;
    }

    WayItem getNearItem(WayItem _item,WayItem _endItem) {
        List<WayItem> _nearList = GetNearItems(_item);
        if (_nearList.Contains(_endItem)) {
            _endItem.lastWay = _item;
            return _endItem;
        }
        WayItem _tmpNearItem = null;
        foreach (WayItem _tmp in _nearList)
        {
            if (!m_openList.Contains(_tmp))
            {
                if (!_tmp.isObstacle)
                {
                    _tmp.lastWay = _item;
                    _tmp.dis_g = _item.dis_g + 2;
                    _tmp.dis_h = Mathf.Pow(_tmp.index_x - _endItem.index_x, 2) + Mathf.Pow(_tmp.index_y - _endItem.index_y, 2);
                    _tmp.dis_f = _tmp.dis_g + _tmp.dis_h;

                    if (_tmp.dis_f > 0f) {
                        if (_tmpNearItem == null)
                        {
                            _tmpNearItem = _tmp;
                        }
                        //else if (_tmp.dis_f < _tmpNearItem.dis_f)
                        else if (_tmp.dis_f < _tmpNearItem.dis_f || (_tmp.dis_f== _tmpNearItem.dis_f && _tmp.dis_h< _tmpNearItem.dis_h))
                        {
                            _tmpNearItem = _tmp;
                        }
                    }
                }
                m_openList.Add(_tmp);
            }
        }
        return _tmpNearItem;
    }

    List<WayItem> m_openList = new List<WayItem>();
    void FindPath(WayItem _startItem,WayItem _endItem) {
        if (_startItem == null || _endItem == null) {
            return;
        }
        if (_startItem.index_x == _endItem.index_x && _startItem.index_y == _endItem.index_y) {
            return;
        }
        m_openList.Clear();
        WayItem _curItem = _startItem;
        m_openList.Add(_startItem);
        while (true) {
            WayItem _tmp = getNearItem(_curItem,_endItem);
            //Debug.LogError(_tmp.name);
            if (_tmp == null) {
                break;
            }
            if (_tmp.index_x == _endItem.index_x && _tmp.index_y == _endItem.index_y)
            {
                break;
            }
            _curItem = _tmp;
        }
        //Debug.LogError("----------------------");
        _curItem = _endItem;
        while (true)
        {
            //Debug.LogError(_curItem);
            if (_curItem.lastWay == null)
            {
                break;
            }
            if(_curItem != _endItem)
            {
                _curItem.SetColor(Color.green);
            }
            _curItem = _curItem.lastWay;
        }
    }
}

经过测试,寻路正常,只是发现偶尔寻路到的路径不是最优解,欢迎各位指正优化。。。。。