像是角色目前在A地點要移動到D地點,按下移動後就會A->B->C->D走到目標,如果地圖並不複雜龐大的話,此時就可以使用簡單的路徑取得,也就是本篇使用的方法,使用暴力搜尋法把所有可能的路徑全部找出來。
雖然在製作上不算太困難,但是既然是使用Unity那就要順便利用一下編輯器的方便性,讓編輯的人可以在Inspector當中直接編輯各個節點連結的資訊,所以就把這個功能製作成一個Component來使用。
public GameObject[] connectedNode; //因為要讓編輯的人可以在Inspector當中編輯節點的資訊,所以這邊要準備一個public陣列,就可以在Unity中把節點的GameObject放進去 public void GetPath(GameObject from, GameObject to, List<List<GameObject>> totalPath, List<GameObject> oldList) { //這邊製作一個方法給前一個來源點跟最終目標點的資訊,以及所有路徑的List跟當前路徑的List,因為是參照所以這個方法就不用回傳值了 ... //這邊可以做一些停止判斷,像是已經加過這個點那就停止這段搜尋返回上一個節點 foreach(GameObject go in connectedNode) { //接著如果上面的判斷都過,接著就從設定好的nodes去取得下一個節點物件,然後取得下一個節點資訊直到停止 GetPath(this.gameObject, to, totalPath, copyList); } }
基本上概念就是做成這樣的Component,這樣做完後把這個Component放到當作節點的空GameObject上就可以用了,但是在編輯的時候會發現一個問題,那就是節點一多起來,有時候會不清楚那些編輯過那些沒有,所以這部分還可以使用OnDrawGizmos()在編輯視窗中加上一些輔助線條來幫助編輯。
void OnDrawGizmos() { foreach (GameObject go in connectedNode) { Gizmos.color = Color.yellow; PathNode node = go.GetComponent(typeof(PathNode)) as PathNode; foreach(GameObject reflect in node.connectedNode) if(reflect == this.gameObject) Gizmos.color = Color.cyan; Gizmos.DrawLine(this.transform.position, go.transform.position); } }基本上就是去跑每個節點,然後使用Gizmos.DrawLine()把點做個連線,這邊多加個判斷是如果只有單向的就顯示黃色,雙向的顯示青色,在編輯上會更清楚那些點還需要編輯。
使用上來說可以給每個節點加上Tag或是預先做好列表,使用Tag的話可以類似這樣。
GameObject from = GameObject.FindGameObjectWithTag("Tag1"); GameObject to = GameObject.FindGameObjectWithTag("Tag2"); List<List<GameObject> totalPath = new List<List<GameObject>(); List<GameObject> newList = new List<GameObject>(); PathNode node = from.GetComponent(typeof(PathNode)) as PathNode; node.GetPath(from, to, totalPath, newList);然後就可以取得totalPath的資訊了,接著就可以看是不是要排序來取得最短路徑這樣。
--
實作測試(選擇起始點跟終點,然後點選GetPath取得路徑,使用Next切換不同的路徑,此部分為所有路徑非最短路徑)
--
路徑取得的Code部分
using UnityEngine; using System.Collections; using System.Collections.Generic; public class PathNode : MonoBehaviour { public GameObject[] connectedNode; //準備一個GameObject陣列,這樣你就可以在Unity inspector當中設定數量跟配置物件 //在編輯器中畫出輔助線條,讓編輯節點更容易一點 void OnDrawGizmos() { foreach (GameObject go in connectedNode) { if(go == null) continue; Gizmos.color = Color.yellow; PathNode node = go.GetComponent(typeof(PathNode)) as PathNode; foreach(GameObject reflect in node.connectedNode) if(reflect == this.gameObject) Gizmos.color = Color.cyan; Gizmos.DrawLine(this.transform.position, go.transform.position); } } public void GetPath(GameObject from, GameObject to, List<List<GameObject>> totalPath, List<GameObject> oldList) { if (oldList.Contains (this.gameObject)) return; //如果目前這個節點已經在List裡了那就回上一個點 oldList.Add (this.gameObject); //把目前這個節點加入List if (this.gameObject == to) { totalPath.Add(oldList); //如果目前這個節點是你的目標點,就把這串List加入totalPath裡面,並回到上一個節點 return; } //尋找下一個節點 for (int i = 0; i < connectedNode.Length; ++i) { if(connectedNode[i] == from) continue; //略過你來的那個節點 //複製一份新的List List<GameObject> copyList = new List<GameObject>(); foreach(GameObject go in oldList) copyList.Add(go); PathNode node = connectedNode[i].GetComponent(typeof(PathNode)) as PathNode; node.GetPath(this.gameObject, to, totalPath, copyList); } } }
接下來如果要取得最短路徑也就不難了,可以直接比較每個路徑的節點數量取最少的,或者是計算節點的距離來取得實際上最短的路徑,都可以隨自己去控制了。
No comments:
Post a Comment