#nullable enable using System.Runtime.InteropServices; using UnityEngine; using ZLinq.Traversables; namespace ZLinq { public static class GameObjectTraverserExtensions { public static GameObjectTraverser AsTraverser(this GameObject origin) => new(origin); // type inference helper public static ValueEnumerable, GameObject> Children(this GameObjectTraverser traverser) => traverser.Children(); public static ValueEnumerable, GameObject> ChildrenAndSelf(this GameObjectTraverser traverser) => traverser.ChildrenAndSelf(); public static ValueEnumerable, GameObject> Descendants(this GameObjectTraverser traverser) => traverser.Descendants(); public static ValueEnumerable, GameObject> DescendantsAndSelf(this GameObjectTraverser traverser) => traverser.DescendantsAndSelf(); public static ValueEnumerable, GameObject> Ancestors(this GameObjectTraverser traverser) => traverser.Ancestors(); public static ValueEnumerable, GameObject> AncestorsAndSelf(this GameObjectTraverser traverser) => traverser.AncestorsAndSelf(); public static ValueEnumerable, GameObject> BeforeSelf(this GameObjectTraverser traverser) => traverser.BeforeSelf(); public static ValueEnumerable, GameObject> BeforeSelfAndSelf(this GameObjectTraverser traverser) => traverser.BeforeSelfAndSelf(); public static ValueEnumerable, GameObject> AfterSelf(this GameObjectTraverser traverser) => traverser.AfterSelf(); public static ValueEnumerable, GameObject> AfterSelfAndSelf(this GameObjectTraverser traverser) => traverser.AfterSelfAndSelf(); // direct shortcut public static ValueEnumerable, GameObject> Children(this GameObject origin) => origin.AsTraverser().Children(); public static ValueEnumerable, GameObject> ChildrenAndSelf(this GameObject origin) => origin.AsTraverser().ChildrenAndSelf(); public static ValueEnumerable, GameObject> Descendants(this GameObject origin) => origin.AsTraverser().Descendants(); public static ValueEnumerable, GameObject> DescendantsAndSelf(this GameObject origin) => origin.AsTraverser().DescendantsAndSelf(); public static ValueEnumerable, GameObject> Ancestors(this GameObject origin) => origin.AsTraverser().Ancestors(); public static ValueEnumerable, GameObject> AncestorsAndSelf(this GameObject origin) => origin.AsTraverser().AncestorsAndSelf(); public static ValueEnumerable, GameObject> BeforeSelf(this GameObject origin) => origin.AsTraverser().BeforeSelf(); public static ValueEnumerable, GameObject> BeforeSelfAndSelf(this GameObject origin) => origin.AsTraverser().BeforeSelfAndSelf(); public static ValueEnumerable, GameObject> AfterSelf(this GameObject origin) => origin.AsTraverser().AfterSelf(); public static ValueEnumerable, GameObject> AfterSelfAndSelf(this GameObject origin) => origin.AsTraverser().AfterSelfAndSelf(); // OfComponent public static ValueEnumerable, TComponent>, TComponent> OfComponent(this ValueEnumerable, GameObject> source) where TComponent : Component => new(new(source.Enumerator)); public static ValueEnumerable, TComponent>, TComponent> OfComponent(this ValueEnumerable, GameObject> source) where TComponent : Component => new(new(source.Enumerator)); public static ValueEnumerable, TComponent>, TComponent> OfComponent(this ValueEnumerable, GameObject> source) where TComponent : Component => new(new(source.Enumerator)); public static ValueEnumerable, TComponent>, TComponent> OfComponent(this ValueEnumerable, GameObject> source) where TComponent : Component => new(new(source.Enumerator)); public static ValueEnumerable, TComponent>, TComponent> OfComponent(this ValueEnumerable, GameObject> source) where TComponent : Component => new(new(source.Enumerator)); } [StructLayout(LayoutKind.Auto)] public struct GameObjectTraverser : ITraverser { static readonly object CalledTryGetNextChild = new object(); static readonly object ParentNotFound = new object(); readonly GameObject gameObject; readonly Transform transform; // cache transform object? initializedState; // CalledTryGetNext or Parent(for sibling operations) int childCount; // self childCount(TryGetNextChild) or parent childCount(TryGetSibling) int index; public GameObjectTraverser(GameObject origin) { this.gameObject = origin; this.transform = gameObject.transform; this.initializedState = null; this.childCount = 0; this.index = 0; } public GameObject Origin => gameObject; public GameObjectTraverser ConvertToTraverser(GameObject next) => new(next); public bool TryGetParent(out GameObject parent) { var tp = transform.parent; if (tp != null) { parent = tp.gameObject; return true; } parent = default!; return false; } public bool TryGetChildCount(out int count) { count = transform.childCount; return true; } public bool TryGetHasChild(out bool hasChild) { hasChild = transform.childCount != 0; return true; } public bool TryGetNextChild(out GameObject child) { if (initializedState == null) { initializedState = CalledTryGetNextChild; childCount = transform.childCount; } if (index < childCount) { child = transform.GetChild(index++).gameObject; return true; } child = default!; return false; } public bool TryGetNextSibling(out GameObject next) { if (initializedState == null) { var tp = transform.parent; if (tp == null) { var scene = transform.gameObject.scene; // check is scene root object if (scene.IsValid()) { initializedState = scene; childCount = scene.rootCount; index = transform.GetSiblingIndex() + 1; } else { initializedState = ParentNotFound; next = default!; return false; } } else { // cache parent and childCount initializedState = tp; childCount = tp.childCount; // parent's childCount index = transform.GetSiblingIndex() + 1; } } else if (initializedState == ParentNotFound) { next = default!; return false; } if (initializedState is Transform parent) { if (index < childCount) { next = parent.GetChild(index++).gameObject; return true; } } else if (initializedState is UnityEngine.SceneManagement.Scene scene) { if (index < childCount) { var list = UnityEngine.Pool.ListPool.Get(); scene.GetRootGameObjects(list); next = list[index++]; UnityEngine.Pool.ListPool.Release(list); return true; } } next = default!; return false; } public bool TryGetPreviousSibling(out GameObject previous) { if (initializedState == null) { var tp = transform.parent; if (tp == null) { var scene = transform.gameObject.scene; // check is scene root object if (scene.IsValid()) { initializedState = scene; childCount = transform.GetSiblingIndex(); index = 0; } else { initializedState = ParentNotFound; previous = default!; return false; } } else { initializedState = tp; childCount = transform.GetSiblingIndex(); // not childCount but means `to` index = 0; // 0 to siblingIndex } } else if (initializedState == ParentNotFound) { previous = default!; return false; } if (initializedState is Transform parent) { if (index < childCount) { previous = parent.GetChild(index++).gameObject; return true; } } else if (initializedState is UnityEngine.SceneManagement.Scene scene) { if (index < childCount) { var list = UnityEngine.Pool.ListPool.Get(); scene.GetRootGameObjects(list); previous = list[index++]; UnityEngine.Pool.ListPool.Release(list); return true; } } previous = default!; return false; } public void Dispose() { } } }