在Unity中定義統一的物件搜尋介面
阿新 • • 發佈:2019-02-08
我們經常要在Unity中以各種方式搜尋物件。比如按名字搜尋、按tag、layer或者是查詢名字為xxx開頭的物件。
本文是介紹以一種統一的介面來搜尋物件。
1、定義統一的搜尋介面
/// <summary> /// 遊戲物件搜尋介面 /// </summary> public interface IGameObjectFinder { /// <summary> /// 搜尋 /// </summary> /// <param name="root">搜尋的開始位置/根節點</param> /// <param name="findResult">搜尋存放的結果</param> void Find(Transform root, List<Transform> findResult); }
2、定義一個使用上面搜尋介面的方法
public class Finder{ /// <summary> /// 查詢指定根節點下符合指定的查詢器的Transform並保持到findResult中 /// </summary> /// <param name="root"></param> /// <param name="findResult"></param> /// <param name="finder"></param> public static void Find(Transform root, List<Transform> findResult, IGameObjectFinder finder) { if (root == null) { throw new Exception("root can not be null, it defines the starting point of the find path"); } if (findResult == null) { throw new Exception("findResult can not be null, it used to collect the find result"); } if (finder == null) { throw new Exception("finder can not be null, it defines how to find transform"); } finder.Find(root, findResult); } }
可以看到,2步驟只是簡單呼叫1的介面進行搜尋。但是1只是介面,有啥用?接著看。
3、實現一個查詢某個元件的介面
/// <summary> /// 根據元件搜尋 /// </summary> /// <typeparam name="T"></typeparam> public class GameObjectFinderByComponent<T> : IGameObjectFinder where T : Component { public void Find(Transform root, List<Transform> findResult) { foreach (var componentsInChild in root.GetComponentsInChildren<T>()) { findResult.Add(componentsInChild.transform); } } }
可以看到只要實現IGameObjectFinder就可以了。那麼如果這時候想呼叫,應該怎麼呼叫呢?比如想查詢transform下面的剛體元件,然後儲存到result裡面。只要:Finder.Find(transform, result, new GameObjectFinderByComponent<Rigidbody>());什麼?看起來好像有點小題大作,直接用GetComponentsInChildren不就好嗎?先不急。我們繼續看看其它查詢需求。比如我想查詢名字為xxx的物件。
4、實現一個迭代查詢
首先,要查詢指定節點下的某個名字的所有節點(不管深度多少),這個需要遍歷。那麼,我們可以先抽象出一個遍歷的介面。為了保證搜尋的統一,那麼我們還是從IGameObjectFinder中繼承。 /// <summary>
/// 迭代遍歷搜尋
/// </summary>
public class GameObjectFinderByIteration : IGameObjectFinder
{
private IGameObjectFinderForIteration finderForIteration;
public GameObjectFinderByIteration(IGameObjectFinderForIteration finderForIteration)
{
this.finderForIteration = finderForIteration;
}
public void Find(Transform root, List<Transform> findResult)
{
for (int i = 0, childCount = root.childCount; i < childCount; i++)
{
Transform t = root.GetChild(i);
if (finderForIteration.isVaild(t))
{
findResult.Add(t);
}
Find(t, findResult);
}
}
}
這個程式碼的意思就是:我先把開始節點下面的每個子節點通過另一個介面IGameObjectFinderForIteration來判斷是否符合要求,是的話則加入結果列表。然後繼續查詢這個子結點下的其他子節點。(該搜尋是不包括第一個開始節點的)IGameObjectFinderForIteration的介面也很簡單:
/// <summary>
/// 迭代搜尋判斷
/// </summary>
public interface IGameObjectFinderForIteration
{
/// <summary>
/// 指定節點是否合法
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
bool isVaild(Transform node);
}
好的,這樣就意味著我們要想查詢某個根節點下的所有子節點(包括直接和間接的),只要實現一個IGameObjectFinderForIteration。那麼OK。看看怎麼按名字搜尋。 /// <summary>
/// 迭代遍歷按名字搜尋
/// </summary>
public class FinderForIterationByName : IGameObjectFinderForIteration
{
protected readonly string NAME;
public FinderForIterationByName(string name)
{
NAME = name;
}
public bool isVaild(Transform getChild)
{
return getChild.gameObject.name.Equals(NAME);
}
}
使用也很簡單,加入想找transform節點下,名字為“abc”的物件,並儲存在result列表裡面。只要這樣:Finder.Find(transform, result, new GameObjectFinderByIteration(new FinderForIterationByName("abc")));