項目概述

LINQ to GameObject是一個為Unity引擎設計的擴展方法庫,它允許開發者使用LINQ(Language Integrated Query)語法來遍歷和操作遊戲對象(GameObject)層級結構。該項目的核心是GameObjectExtensions靜態類,通過將層級遍歷操作抽象為可枚舉序列,極大簡化了Unity場景中複雜對象樹的查詢與操作邏輯。

項目結構遵循功能模塊化設計,主要源碼文件位於Assets/LINQtoGameObject/Scripts/目錄下,包含三個核心擴展方法文件和示例代碼:

  • 遍歷核心:GameObjectExtensions.Traverse.cs
  • 集合操作:GameObjectExtensions.Enumerable.cs
  • 對象操作:GameObjectExtensions.Operate.cs
  • 使用示例:SampleSceneScript.cs

架構設計理念

樹形結構的軸遍歷模型

LINQ to GameObject的核心設計理念是將遊戲對象層級視為多軸樹結構,通過不同方向的遍歷軸實現靈活查詢。官方文檔中定義了五種基本遍歷軸:

Linq之擴展方法_API

  • 向上軸(Ancestors):從當前對象遍歷至根節點
  • 向下軸(Descendants):從當前對象遍歷所有子節點
  • 同級軸(BeforeSelf/AfterSelf):遍歷同層級的前後兄弟對象
  • 父子軸(Parent/Children):直接父子關係遍歷

這種模型使得開發者可以通過類似文件系統路徑的方式定位對象,例如:

// 獲取所有父節點
var ancestors = origin.Ancestors();  
// 獲取直接子節點
var children = origin.Children();    
// 獲取所有後代節點
var descendants = origin.Descendants();

延遲執行與流式處理

所有遍歷方法均返回IEnumerable<GameObject>接口,採用延遲執行(Deferred Execution)策略。這意味着查詢表達式在定義時不會立即執行,而是在枚舉結果時才真正遍歷對象樹,從而提高性能並支持鏈式查詢:

// 鏈式查詢:篩選名稱以"B"結尾的子對象並銷燬
origin.Children()
      .Where(x => x.name.EndsWith("B"))
      .Destroy();

核心擴展方法實現

1. 層級遍歷系統(Traverse.cs)

GameObjectExtensions.Traverse.cs定義了所有基礎遍歷方法,採用結構體枚舉器(Struct Enumerator)實現零GC分配。核心實現包含:

自定義可枚舉類型

為每種遍歷軸實現專用的可枚舉類型,例如ChildrenEnumerableAncestorsEnumerable

public struct ChildrenEnumerable : IEnumerable<GameObject>
{
    readonly GameObject origin;
    readonly bool withSelf;
    
    public Enumerator GetEnumerator()
    {
        return (origin == null) 
            ? new Enumerator(null, withSelf, false) 
            : new Enumerator(origin.transform, withSelf, true);
    }
    
    // 結構體枚舉器實現
    public struct Enumerator : IEnumerator<GameObject>
    {
        readonly Transform originTransform;
        int currentIndex;
        GameObject current;
        
        public bool MoveNext()
        {
            // 核心遍歷邏輯
            if (withSelf)
            {
                current = originTransform.gameObject;
                withSelf = false;
                return true;
            }
            
            if (currentIndex < originTransform.childCount)
            {
                current = originTransform.GetChild(currentIndex++).gameObject;
                return true;
            }
            return false;
        }
    }
}
關鍵遍歷方法

方法

功能描述

Parent()

獲取直接父對象

Child(string name)

根據名稱獲取子對象

Children()

獲取所有直接子對象

Ancestors()

獲取所有祖先對象

Descendants(Func<Transform, bool> filter)

獲取所有後代對象,支持遍歷過濾

BeforeSelf()/AfterSelf()

獲取前後兄弟對象

2. 集合操作擴展(Enumerable.cs)

GameObjectExtensions.Enumerable.cs為IEnumerable<GameObject>提供了批量操作方法,主要包括:

組件查詢

OfComponent<T>()方法實現了從對象集合中提取特定組件的功能,針對Unity編輯器和運行時做了不同優化:

public static IEnumerable<T> OfComponent<T>(this IEnumerable<GameObject> source)
    where T : UnityEngine.Component
{
    foreach (var item in source)
    {
#if UNITY_EDITOR
        // 編輯器模式使用緩存列表減少分配
        var cache = ComponentCache<T>.Instance;
        item.GetComponents<T>(cache);
        if (cache.Count != 0)
        {
            yield return cache[0];
            cache.Clear();
        }
#else
        // 運行時直接獲取組件
        var component = item.GetComponent<T>();
        if (component != null)
        {
            yield return component;
        }
#endif
    }
}
非分配式數組轉換

為避免頻繁內存分配,實現了ToArrayNonAlloc方法,支持數組複用:

public static int ToArrayNonAlloc<T>(this IEnumerable<T> source, ref T[] array)
{
    var index = 0;
    foreach (var item in source)
    {
        if (array.Length == index)
        {
            // 動態擴容(初始4個元素,之後翻倍)
            var newSize = (index == 0) ? 4 : index * 2;
            Array.Resize(ref array, newSize);
        }
        array[index++] = item;
    }
    return index;
}

3. 對象操作方法(Operate.cs)

GameObjectExtensions.Operate.cs提供了遊戲對象的添加、移動和銷燬操作,核心特性包括:

變換操作類型

定義了兩種變換操作枚舉,控制子對象添加/移動時的變換屬性處理:

// 克隆對象的變換處理方式
public enum TransformCloneType
{
    KeepOriginal,  // 保留原始變換
    FollowParent,  // 繼承父對象變換
    Origin,        // 重置為原點變換
    DoNothing      // 不改變變換
}

// 移動對象的變換處理方式
public enum TransformMoveType
{
    FollowParent,  // 繼承父對象變換
    Origin,        // 重置為原點變換
    DoNothing      // 不改變變換
}
批量對象操作

實現了豐富的對象添加和移動方法,支持批量操作和層級位置控制:

// 添加為最後一個子對象
public static T Add<T>(this GameObject parent, T childOriginal, TransformCloneType cloneType = ...)

// 添加為第一個子對象
public static T AddFirst<T>(this GameObject parent, T childOriginal, ...)

// 在當前對象前添加同級對象
public static T AddBeforeSelf<T>(this GameObject parent, T childOriginal, ...)

// 移動對象到目標父對象
public static T MoveToLast<T>(this GameObject parent, T child, TransformMoveType moveType = ...)

性能優化策略

結構體枚舉器

所有遍歷方法均使用結構體枚舉器(Struct Enumerator)而非類枚舉器,避免了堆內存分配和垃圾回收:

// 結構體枚舉器(值類型)
public struct Enumerator : IEnumerator<GameObject>
{
    // 字段直接存儲狀態,無堆分配
    readonly Transform originTransform;
    int currentIndex;
    GameObject current;
    // ...
}

編輯器與運行時優化

通過條件編譯為Unity編輯器和運行時提供不同實現:

  • 編輯器模式:使用靜態緩存列表減少重複分配
  • 運行時:直接操作提高性能
#if UNITY_EDITOR
// 編輯器模式使用緩存列表
static List<T> componentCache = new List<T>();
item.GetComponents<T>(componentCache);
if (componentCache.Count != 0)
{
    yield return componentCache[0];
    componentCache.Clear();
}
#else
// 運行時直接獲取組件
var component = item.GetComponent<T>();
if (component != null) yield return component;
#endif

非分配式API

提供ToArrayNonAlloc系列方法,支持數組複用,特別適合頻繁執行的遍歷操作:

// 複用數組避免分配
GameObject[] array = new GameObject[0];
void Update()
{
    // 重複使用array,無內存分配
    var size = origin.Children().ToArrayNonAlloc(ref array);
    for (int i = 0; i < size; i++)
    {
        Process(array[i]);
    }
}

示例代碼分析

SampleSceneScript.cs展示了庫的典型用法,包括遍歷查詢、組件篩選和批量操作:

基礎遍歷示例

// 獲取所有祖先對象
foreach (var item in origin.Ancestors())
{
    Debug.Log(item.name);
}

// 篩選名稱以"B"結尾的子對象
var filter = origin.Children().Where(x => x.name.EndsWith("B"));

組件查詢示例

// 獲取所有後代對象中的SphereCollider組件
var spheres = origin.Descendants().OfComponent<SphereCollider>();
foreach (var item in spheres)
{
    Debug.Log("Sphere:" + item.name + " Radius:" + item.radius);
}

批量添加對象

// 批量添加子對象
origin.AddRange(new[] { new GameObject("lastChild1"), 
                       new GameObject("lastChild2"), 
                       new GameObject("lastChild3") });

// 添加到特定位置
origin.AddBeforeSelf(new GameObject("beforeSelf0"));
origin.AddAfterSelf(new GameObject("afterSelf0"));

總結與擴展

LINQ to GameObject通過擴展方法和LINQ語法,將複雜的遊戲對象層級操作轉化為直觀的查詢表達式,主要優勢包括:

  1. 簡化代碼:將多層嵌套的遍歷邏輯簡化為鏈式查詢
  2. 提高性能:結構體枚舉器和非分配API減少垃圾回收
  3. 靈活擴展:模塊化設計支持自定義遍歷和操作
  4. 統一接口:一致的API設計降低學習成本

開發者可以通過擴展GameObjectExtensions類添加自定義操作,或基於現有遍歷方法構建更復雜的查詢邏輯。項目的設計思路也可借鑑到其他樹形結構的操作場景,如UI層級管理、場景對象池等。