有段时间没用到了,最近看的项目中涉及到寻路,就花点时间自己写了一个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;
}
}
}
经过测试,寻路正常,只是发现偶尔寻路到的路径不是最优解,欢迎各位指正优化。。。。。