生活随笔
收集整理的這篇文章主要介紹了
Unity 之 手把手教你实现自己Unity2D游戏寻路逻辑 【文末源码】
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Unity 之 手把手教你實現自己Unity2D游戲尋路邏輯 【文末源碼】
- 前言
- 一,效果展示
- 二,場景搭建
- 三,代碼邏輯
- 四,完善場景
- 五,使用小結
前言
還在看別人的尋路邏輯?保姆級教程,一步步教你實現網格尋路邏輯。 超級詳細的代碼注釋,圖文步驟詳解。寫文不易,有幫助的話三連支持下吧~
一,效果展示
二,場景搭建
以一個 9 * 9 的地圖為例:
新建工程,設置屏幕分辨率為: [1080 * 1920],如下圖:
在自帶場景下創建Image作為背景(在Hierarchy右鍵 --> UI --> Image); 修改其名字為”GridManager“,坐標為: [0, 0, 0],大小為:[1000 * 1000] 并將顏色設置為黑色,完成后效果如下圖:
在"GridManager"下面創建一個空物體,命名為"Grid",將其錨點設置為鋪滿,并為其添加組件Grid Layout Group,完成后效果如下圖:
在"Grid"下面添加一個Image,并為其添加Button組件,作為點擊格子使用,然后Ctrl + D 復制80個,實現效果如下:
調整"Grid"的Grid Layout Group組件屬性值,Left,Top,SpacingX,Y 均設置為10,意思為左邊距為10,上邊距為10,物體間隔為10,實現后效果如下:
現在這樣就已經模擬搭建出類似棋盤場景了,下面看下代碼改如何是實現的吧。
三,代碼邏輯
public enum Direction
{up
, down
, left
, right
}public class RoutingObject : MonoBehaviour
{public int x
;public int y
;public int targetDistance
;public int moveDistance
;public int moveSum
;public bool isCanMove
;public Direction direction
;
}
- 尋路邏輯:根據傳入參數(起始點,結束點,地圖),進行查找可移動路線,最后篩選出最短路線。部分代碼如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Routing
{#region 單例Routing() { }static Routing instance
;public static Routing Instance
{get{if (instance
== null){instance
= new Routing();}return instance
;}}#endregionRoutingObject[,] map
;List<RoutingObject> open
= new List<RoutingObject>();List<RoutingObject> closed
= new List<RoutingObject>();List<RoutingObject> route
= new List<RoutingObject>();void Init(RoutingObject[,] mapArray
){open
.Clear();closed
.Clear();route
.Clear();map
= mapArray
;}public bool IsRouting(RoutingObject start
, RoutingObject end
, RoutingObject[,] mapArray
){Init(mapArray
);Explore(start
, end
, start
);return route
.Count
> 0;}void Explore(RoutingObject center
, RoutingObject end
, RoutingObject start
){closed
.Add(center
);if (open
.Contains(center
)){open
.Remove(center
);}if (IsGetEnd(end
)){ReturnRoute(end
, start
);}else{if (center
.y
- 1 >= 0){RoutingObject up
= map
[center
.x
, center
.y
- 1];GetMoveSumByDirection(up
, center
, end
, Direction
.up
);}if (center
.y
+ 1 < GridManager
.Instance
.mapColumnCount
){RoutingObject down
= map
[center
.x
, center
.y
+ 1];GetMoveSumByDirection(down
, center
, end
, Direction
.down
);}if (center
.x
- 1 >= 0){RoutingObject left
= map
[center
.x
- 1, center
.y
];GetMoveSumByDirection(left
, center
, end
, Direction
.left
);}if (center
.x
+ 1 < GridManager
.Instance
.mapRowCount
){RoutingObject right
= map
[center
.x
+ 1, center
.y
];GetMoveSumByDirection(right
, center
, end
, Direction
.right
);}if (open
.Count
> 0){RoutingObject ro
= GetMinimumMoveSum();Explore(ro
, end
, start
);}else{Debug
.Log("沒有找到目標點");}}}void GetMoveSumByDirection(RoutingObject center
, RoutingObject start
, RoutingObject end
, Direction direction
){if (IsForward(center
)){center
.direction
= direction
;center
.moveDistance
= GetDistance(center
, start
);center
.targetDistance
= GetDistance(center
, end
);center
.moveSum
= center
.moveDistance
+ center
.targetDistance
;open
.Add(center
);}else{}}bool IsForward(RoutingObject ro
){if (closed
.Contains(ro
) || open
.Contains(ro
)){return false;}else{if (ro
.isCanMove
){return true;}else{closed
.Add(ro
);return false;}}}int GetDistance(RoutingObject start
, RoutingObject end
){return Mathf
.Abs(start
.x
- end
.x
) + Mathf
.Abs(start
.y
- end
.y
);}bool IsGetEnd(RoutingObject end
){return closed
.Contains(end
);}RoutingObject GetMinimumMoveSum(){RoutingObject ro
= null;RoutingObject temporary
= new RoutingObject();for (int i
= 0; i
< open
.Count
; i
++){if (i
== 0){ro
= open
[i
];temporary
= open
[i
];}else{if (open
[i
].moveSum
< temporary
.moveSum
){ro
= open
[i
];temporary
= open
[i
];}}}return ro
;}void ReturnRoute(RoutingObject center
, RoutingObject start
){route
.Add(center
);if (!route
.Contains(start
)){switch (center
.direction
){case Direction
.up
:ReturnRoute(map
[center
.x
, center
.y
+ 1], start
);break;case Direction
.down
:ReturnRoute(map
[center
.x
, center
.y
- 1], start
);break;case Direction
.left
:ReturnRoute(map
[center
.x
+ 1, center
.y
], start
);break;case Direction
.right
:ReturnRoute(map
[center
.x
- 1, center
.y
], start
);break;}}else{RouteSort(start
);}}void RouteSort(RoutingObject start
){List<RoutingObject> list
= new List<RoutingObject>(route
);route
.Clear();for (int i
= list
.Count
- 1; i
>= 0; i
--){if (list
[i
] != start
){route
.Add(list
[i
]);}}}public List<RoutingObject> GetRoute(){return route
;}
}
- 創建GridManager腳本,將其掛載到GridManager物體上,
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class GridManager : MonoBehaviour
{public static GridManager Instance
;public int mapColumnCount
= 9;public int mapRowCount
= 9;public List<RoutingObject> routeList
= new List<RoutingObject>();public List<GameObject> OccupyGridList
= new List<GameObject>();private GameObject[,] GridArray
;private GameObject selectGrid
;private void Awake(){Instance
= this;GridArray
= new GameObject[mapRowCount
, mapColumnCount
];}void Start(){}public void SetGridArray(GameObject go
, int x
, int y
){GridArray
[x
, y
] = go
;}GameObject GetGridArray(int x
, int y
){return GridArray
[x
, y
];}Grid[,] GetMoveMap(){Grid[,] array
= new Grid[mapRowCount
, mapColumnCount
];for (int i
= 0; i
< mapRowCount
; i
++){for (int j
= 0; j
< mapColumnCount
; j
++){if (OccupyGridList
.Contains(GridArray
[i
, j
])){GridArray
[i
, j
].GetComponent<Grid>().isCanMove
= false;}else{GridArray
[i
, j
].GetComponent<Grid>().isCanMove
= true;}array
[i
, j
] = GridArray
[i
, j
].GetComponent<Grid>();}}return array
;}public void OnClickGrid(GameObject selectObj
){if (selectGrid
== null){selectGrid
= selectObj
;}else{RoutingObject[,] map
= GetMoveMap();RoutingObject start
= selectGrid
.GetComponent<RoutingObject>();RoutingObject end
= selectObj
.GetComponent<RoutingObject>();if (Routing
.Instance
.IsRouting(start
, end
, map
)){Debug
.Log("判斷可以通過");start
.gameObject
.GetComponent<Image>().color
= Color
.cyan
;routeList
.AddRange(Routing
.Instance
.GetRoute());MoveBall();}else{Debug
.LogError("不能移動到當前位置...");}selectGrid
= null;}}void MoveBall(){StartCoroutine(MoveGrid());}IEnumerator MoveGrid(){for (int i
= 0; i
< routeList
.Count
; i
++){GameObject go
= GetGridArray(routeList
[i
].x
, routeList
[i
].y
);go
.GetComponent<Image>().color
= Color
.green
;yield return new WaitForSeconds(0.2f);}routeList
.Clear();}
}
- 創建Grid 腳本名并將其繼承RoutingObject;此腳本需掛載到場景中物體名為‘Grid’的81一個子物體上,主要邏輯:初始計算自身所在二維數組中的位置(左上角為0,0點),給自身添加點擊監聽。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class Grid : RoutingObject
{private void Start(){x
= transform
.GetSiblingIndex() / GridManager
.Instance
.mapColumnCount
;y
= transform
.GetSiblingIndex() % GridManager
.Instance
.mapRowCount
;GridManager
.Instance
.SetGridArray(gameObject
, x
, y
);GetComponent<Button>().onClick
.AddListener(OnClick
);}void OnClick(){Debug
.Log("點擊格子: " + x
+ "," + y
);GridManager
.Instance
.OnClickGrid(gameObject
);}}
四,完善場景
完成上面步驟就已經完成運行測試了,隨便點擊兩個點警徽得到如下結果:
重復運行即可測試無障礙物的情況了,下面我們手動添加下障礙物。
思路:
- 添加Toggle,勾選Toggle,添加/刪除障礙物;
- 添加Button,還原當前表格狀態,方便測試時不用重新運行
在GridManager 腳本中添加如下代碼:
public Toggle AddOccupyToggle
;
public Button ClearButton
;void Start()
{ClearButton
.onClick
.AddListener(ClearFun
);
}
void AddOccupyGridList(GameObject go
)
{Debug
.Log("加入或移除障礙物");if (OccupyGridList
.Contains(go
)){go
.GetComponent<Image>().color
= Color
.white
;OccupyGridList
.Remove(go
);}else{go
.GetComponent<Image>().color
= Color
.red
;OccupyGridList
.Add(go
);}
}
void ClearFun()
{Debug
.Log("清空所有格子的狀態...");selectGrid
= null;OccupyGridList
.Clear();for (int i
= 0; i
< mapRowCount
; i
++){for (int j
= 0; j
< mapColumnCount
; j
++){GameObject go
= GridArray
[i
, j
];go
.GetComponent<Image>().color
= Color
.white
;}}
}
在OnClickGrid() 中添加點擊格子觸發邏輯
代碼處理完成后,在Inspector面板給AddOccupyToggle 和 ClearButton 賦值如下圖:
運行測試:
五,使用小結
經過上的一系列操作就完成了2D網格尋路邏輯,使用時你只需要拷貝RoutingObject和Routing兩個代碼即可。
使用方式:調用Routing.Instance.IsRouting(start, end, map)校驗是否能通過,若能通過則通過Routing.Instance.GetRoute() 獲取最短路徑,提供個實際邏輯使用即可。
說好的:文末源碼;沒有積分的童鞋,可以關注下面👇🏻 卡片公號,回復【2D尋路】獲取完整源碼吧~
總結
以上是生活随笔為你收集整理的Unity 之 手把手教你实现自己Unity2D游戏寻路逻辑 【文末源码】的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。