Files
EnglewoodLAB/Assets/Scripts/UVC/Linq/GameObjectExtensions.Traverse.cs

2654 lines
95 KiB
C#
Raw Permalink Normal View History

2026-01-20 11:27:56 +09:00
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UVC.Linq
{
public static partial class GameObjectExtensions
{
/// 축(부모, 자식, 자식들, 조상/자손, 이전 형제/이후 형제 객체)을 기반으로 게임 객체를 순회합니다.
/// <summary>이 GameObject의 부모 GameObject를 가져옵니다. 이 GameObject에 부모가 없으면 null을 반환합니다.</summary>
public static GameObject Parent(this GameObject origin)
{
if (origin == null) return null;
var parentTransform = origin.transform.parent;
if (parentTransform == null) return null;
return parentTransform.gameObject;
}
/// <summary>지정된 이름을 가진 첫 번째 자식 GameObject를 가져옵니다. 지정된 이름을 가진 GameObject가 없으면 null을 반환합니다.</summary>
public static GameObject Child(this GameObject origin, string name)
{
if (origin == null) return null;
var child = origin.transform.Find(name); // transform.find는 비활성 객체를 가져올 수 있습니다.
if (child == null) return null;
return child.gameObject;
}
/// <summary>자식 GameObject의 컬렉션을 반환합니다.</summary>
public static ChildrenEnumerable Children(this GameObject origin)
{
return new ChildrenEnumerable(origin, false);
}
/// <summary>이 GameObject와 자식 GameObject를 포함하는 GameObject 컬렉션을 반환합니다.</summary>
public static ChildrenEnumerable ChildrenAndSelf(this GameObject origin)
{
return new ChildrenEnumerable(origin, true);
}
/// <summary>이 GameObject의 조상 GameObject 컬렉션을 반환합니다.</summary>
public static AncestorsEnumerable Ancestors(this GameObject origin)
{
return new AncestorsEnumerable(origin, false);
}
/// <summary>이 요소와 이 GameObject의 조상을 포함하는 GameObject 컬렉션을 반환합니다.</summary>
public static AncestorsEnumerable AncestorsAndSelf(this GameObject origin)
{
return new AncestorsEnumerable(origin, true);
}
/// <summary>자손 GameObject의 컬렉션을 반환합니다.</summary>
public static DescendantsEnumerable Descendants(this GameObject origin, Func<Transform, bool> descendIntoChildren = null)
{
return new DescendantsEnumerable(origin, false, descendIntoChildren);
}
/// <summary>이 GameObject와 이 GameObject의 모든 자손 GameObject를 포함하는 GameObject 컬렉션을 반환합니다.</summary>
public static DescendantsEnumerable DescendantsAndSelf(this GameObject origin, Func<Transform, bool> descendIntoChildren = null)
{
return new DescendantsEnumerable(origin, true, descendIntoChildren);
}
/// <summary>이 GameObject 이전의 형제 GameObject 컬렉션을 반환합니다.</summary>
public static BeforeSelfEnumerable BeforeSelf(this GameObject origin)
{
return new BeforeSelfEnumerable(origin, false);
}
/// <summary>이 GameObject와 이 GameObject 이전의 형제 GameObject를 포함하는 GameObject 컬렉션을 반환합니다.</summary>
public static BeforeSelfEnumerable BeforeSelfAndSelf(this GameObject origin)
{
return new BeforeSelfEnumerable(origin, true);
}
/// <summary>이 GameObject 이후의 형제 GameObject 컬렉션을 반환합니다.</summary>
public static AfterSelfEnumerable AfterSelf(this GameObject origin)
{
return new AfterSelfEnumerable(origin, false);
}
/// <summary>이 GameObject와 이 GameObject 이후의 형제 GameObject를 포함하는 GameObject 컬렉션을 반환합니다.</summary>
public static AfterSelfEnumerable AfterSelfAndSelf(this GameObject origin)
{
return new AfterSelfEnumerable(origin, true);
}
// 수동 구조체 열거자를 구현합니다.
public struct ChildrenEnumerable : IEnumerable<GameObject>
{
readonly GameObject origin;
readonly bool withSelf;
public ChildrenEnumerable(GameObject origin, bool withSelf)
{
this.origin = origin;
this.withSelf = withSelf;
}
/// <summary>소스 컬렉션에서 지정된 컴포넌트의 컬렉션을 반환합니다.</summary>
public OfComponentEnumerable<T> OfComponent<T>()
where T : Component
{
return new OfComponentEnumerable<T>(ref this);
}
/// <summary>소스 컬렉션의 모든 GameObject를 안전하게(null 확인) 파괴합니다.</summary>
/// <param name="useDestroyImmediate">에디터 모드인 경우 true이거나 !Application.isPlaying을 전달해야 합니다.</param>
/// <param name="detachParent">parent = null로 설정합니다.</param>
public void Destroy(bool useDestroyImmediate = false, bool detachParent = false)
{
var e = GetEnumerator();
while (e.MoveNext())
{
e.Current.Destroy(useDestroyImmediate, false);
}
if (detachParent)
{
origin.transform.DetachChildren();
if (withSelf)
{
#if !(UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5)
origin.transform.SetParent(null);
#else
origin.transform.parent = null;
#endif
}
}
}
/// <summary>소스 컬렉션의 모든 GameObject를 안전하게(null 확인) 파괴합니다.</summary>
/// <param name="useDestroyImmediate">에디터 모드인 경우 true이거나 !Application.isPlaying을 전달해야 합니다.</param>
public void Destroy(Func<GameObject, bool> predicate, bool useDestroyImmediate = false)
{
var e = GetEnumerator();
while (e.MoveNext())
{
var item = e.Current;
if (predicate(item))
{
item.Destroy(useDestroyImmediate, false);
}
}
}
public Enumerator GetEnumerator()
{
// GetEnumerator 시점에만 GameObject가 파괴되었는지 확인합니다.
return (origin == null)
? new Enumerator(null, withSelf, false)
: new Enumerator(origin.transform, withSelf, true);
}
IEnumerator<GameObject> IEnumerable<GameObject>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#region LINQ
int GetChildrenSize()
{
return origin.transform.childCount + (withSelf ? 1 : 0);
}
public void ForEach(Action<GameObject> action)
{
var e = this.GetEnumerator();
while (e.MoveNext())
{
action(e.Current);
}
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc(ref GameObject[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
if (array.Length == index)
{
var newSize = (index == 0) ? GetChildrenSize() : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = item;
}
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc(Func<GameObject, bool> filter, ref GameObject[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
if (!filter(item)) continue;
if (array.Length == index)
{
var newSize = (index == 0) ? GetChildrenSize() : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = item;
}
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc<T>(Func<GameObject, T> selector, ref T[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
if (array.Length == index)
{
var newSize = (index == 0) ? GetChildrenSize() : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = selector(item);
}
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc<T>(Func<GameObject, bool> filter, Func<GameObject, T> selector, ref T[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
if (!filter(item)) continue;
if (array.Length == index)
{
var newSize = (index == 0) ? GetChildrenSize() : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = selector(item);
}
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc<TState, T>(Func<GameObject, TState> let, Func<TState, bool> filter, Func<TState, T> selector, ref T[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
var state = let(item);
if (!filter(state)) continue;
if (array.Length == index)
{
var newSize = (index == 0) ? GetChildrenSize() : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = selector(state);
}
return index;
}
public GameObject[] ToArray()
{
var array = new GameObject[GetChildrenSize()];
var len = ToArrayNonAlloc(ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public GameObject[] ToArray(Func<GameObject, bool> filter)
{
var array = new GameObject[GetChildrenSize()];
var len = ToArrayNonAlloc(filter, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public T[] ToArray<T>(Func<GameObject, T> selector)
{
var array = new T[GetChildrenSize()];
var len = ToArrayNonAlloc<T>(selector, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public T[] ToArray<T>(Func<GameObject, bool> filter, Func<GameObject, T> selector)
{
var array = new T[GetChildrenSize()];
var len = ToArrayNonAlloc(filter, selector, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public T[] ToArray<TState, T>(Func<GameObject, TState> let, Func<TState, bool> filter, Func<TState, T> selector)
{
var array = new T[GetChildrenSize()];
var len = ToArrayNonAlloc(let, filter, selector, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public GameObject First()
{
var e = this.GetEnumerator();
if (e.MoveNext())
{
return e.Current;
}
else
{
throw new InvalidOperationException("시퀀스가 비어 있습니다.");
}
}
public GameObject FirstOrDefault()
{
var e = this.GetEnumerator();
return (e.MoveNext())
? e.Current
: null;
}
#endregion
public struct Enumerator : IEnumerator<GameObject>
{
readonly int childCount; // childCount는 GetEnumerator가 호출될 때 고정됩니다.
readonly Transform originTransform;
readonly bool canRun;
bool withSelf;
int currentIndex;
GameObject current;
internal Enumerator(Transform originTransform, bool withSelf, bool canRun)
{
this.originTransform = originTransform;
this.withSelf = withSelf;
this.childCount = canRun ? originTransform.childCount : 0;
this.currentIndex = -1;
this.canRun = canRun;
this.current = null;
}
public bool MoveNext()
{
if (!canRun) return false;
if (withSelf)
{
current = originTransform.gameObject;
withSelf = false;
return true;
}
currentIndex++;
if (currentIndex < childCount)
{
var child = originTransform.GetChild(currentIndex);
current = child.gameObject;
return true;
}
return false;
}
public GameObject Current { get { return current; } }
object IEnumerator.Current { get { return current; } }
public void Dispose() { }
public void Reset() { throw new NotSupportedException(); }
}
public struct OfComponentEnumerable<T> : IEnumerable<T>
where T : Component
{
ChildrenEnumerable parent;
public OfComponentEnumerable(ref ChildrenEnumerable parent)
{
this.parent = parent;
}
public OfComponentEnumerator<T> GetEnumerator()
{
return new OfComponentEnumerator<T>(ref this.parent);
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#region LINQ
public void ForEach(Action<T> action)
{
var e = this.GetEnumerator();
while (e.MoveNext())
{
action(e.Current);
}
}
public T First()
{
var e = this.GetEnumerator();
if (e.MoveNext())
{
return e.Current;
}
else
{
throw new InvalidOperationException("시퀀스가 비어 있습니다.");
}
}
public T FirstOrDefault()
{
var e = this.GetEnumerator();
return (e.MoveNext())
? e.Current
: null;
}
public T[] ToArray()
{
var array = new T[parent.GetChildrenSize()];
var len = ToArrayNonAlloc(ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc(ref T[] array)
{
var index = 0;
var e = this.GetEnumerator();
while (e.MoveNext())
{
if (array.Length == index)
{
var newSize = (index == 0) ? parent.GetChildrenSize() : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = e.Current;
}
return index;
}
#endregion
}
public struct OfComponentEnumerator<T> : IEnumerator<T>
where T : Component
{
Enumerator enumerator; // 열거자는 변경 가능합니다.
T current;
#if UNITY_EDITOR
static List<T> componentCache = new List<T>(); // UNITY_EDITOR에서 할당 없음을 위함
#endif
public OfComponentEnumerator(ref ChildrenEnumerable parent)
{
this.enumerator = parent.GetEnumerator();
this.current = default(T);
}
public bool MoveNext()
{
while (enumerator.MoveNext())
{
#if UNITY_EDITOR
enumerator.Current.GetComponents<T>(componentCache);
if (componentCache.Count != 0)
{
current = componentCache[0];
componentCache.Clear();
return true;
}
#else
var component = enumerator.Current.GetComponent<T>();
if (component != null)
{
current = component;
return true;
}
#endif
}
return false;
}
public T Current { get { return current; } }
object IEnumerator.Current { get { return current; } }
public void Dispose() { }
public void Reset() { throw new NotSupportedException(); }
}
}
public struct AncestorsEnumerable : IEnumerable<GameObject>
{
readonly GameObject origin;
readonly bool withSelf;
public AncestorsEnumerable(GameObject origin, bool withSelf)
{
this.origin = origin;
this.withSelf = withSelf;
}
/// <summary>소스 컬렉션에서 지정된 컴포넌트의 컬렉션을 반환합니다.</summary>
public OfComponentEnumerable<T> OfComponent<T>()
where T : Component
{
return new OfComponentEnumerable<T>(ref this);
}
/// <summary>소스 컬렉션의 모든 GameObject를 안전하게(null 확인) 파괴합니다.</summary>
/// <param name="useDestroyImmediate">에디터 모드인 경우 true이거나 !Application.isPlaying을 전달해야 합니다.</param>
public void Destroy(bool useDestroyImmediate = false)
{
var e = GetEnumerator();
while (e.MoveNext())
{
e.Current.Destroy(useDestroyImmediate, false);
}
}
/// <summary>소스 컬렉션의 모든 GameObject를 안전하게(null 확인) 파괴합니다.</summary>
/// <param name="useDestroyImmediate">에디터 모드인 경우 true이거나 !Application.isPlaying을 전달해야 합니다.</param>
public void Destroy(Func<GameObject, bool> predicate, bool useDestroyImmediate = false)
{
var e = GetEnumerator();
while (e.MoveNext())
{
var item = e.Current;
if (predicate(item))
{
item.Destroy(useDestroyImmediate, false);
}
}
}
public Enumerator GetEnumerator()
{
// GetEnumerator 시점에만 GameObject가 파괴되었는지 확인합니다.
return (origin == null)
? new Enumerator(null, null, withSelf, false)
: new Enumerator(origin, origin.transform, withSelf, true);
}
IEnumerator<GameObject> IEnumerable<GameObject>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#region LINQ
public void ForEach(Action<GameObject> action)
{
var e = this.GetEnumerator();
while (e.MoveNext())
{
action(e.Current);
}
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc(ref GameObject[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = item;
}
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc(Func<GameObject, bool> filter, ref GameObject[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
if (!filter(item)) continue;
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = item;
}
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc<T>(Func<GameObject, T> selector, ref T[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = selector(item);
}
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc<T>(Func<GameObject, bool> filter, Func<GameObject, T> selector, ref T[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
if (!filter(item)) continue;
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = selector(item);
}
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc<TState, T>(Func<GameObject, TState> let, Func<TState, bool> filter, Func<TState, T> selector, ref T[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
var state = let(item);
if (!filter(state)) continue;
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = selector(state);
}
return index;
}
public GameObject[] ToArray()
{
var array = new GameObject[4];
var len = ToArrayNonAlloc(ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public GameObject[] ToArray(Func<GameObject, bool> filter)
{
var array = new GameObject[4];
var len = ToArrayNonAlloc(filter, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public T[] ToArray<T>(Func<GameObject, T> selector)
{
var array = new T[4];
var len = ToArrayNonAlloc<T>(selector, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public T[] ToArray<T>(Func<GameObject, bool> filter, Func<GameObject, T> selector)
{
var array = new T[4];
var len = ToArrayNonAlloc(filter, selector, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public T[] ToArray<TState, T>(Func<GameObject, TState> let, Func<TState, bool> filter, Func<TState, T> selector)
{
var array = new T[4];
var len = ToArrayNonAlloc(let, filter, selector, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public GameObject First()
{
var e = this.GetEnumerator();
if (e.MoveNext())
{
return e.Current;
}
else
{
throw new InvalidOperationException("시퀀스가 비어 있습니다.");
}
}
public GameObject FirstOrDefault()
{
var e = this.GetEnumerator();
return (e.MoveNext())
? e.Current
: null;
}
#endregion
public struct Enumerator : IEnumerator<GameObject>
{
readonly bool canRun;
GameObject current;
Transform currentTransform;
bool withSelf;
internal Enumerator(GameObject origin, Transform originTransform, bool withSelf, bool canRun)
{
this.current = origin;
this.currentTransform = originTransform;
this.withSelf = withSelf;
this.canRun = canRun;
}
public bool MoveNext()
{
if (!canRun) return false;
if (withSelf)
{
// withSelf, origin 및 originTransform 사용
withSelf = false;
return true;
}
var parentTransform = currentTransform.parent;
if (parentTransform != null)
{
current = parentTransform.gameObject;
currentTransform = parentTransform;
return true;
}
return false;
}
public GameObject Current { get { return current; } }
object IEnumerator.Current { get { return current; } }
public void Dispose() { }
public void Reset() { throw new NotSupportedException(); }
}
public struct OfComponentEnumerable<T> : IEnumerable<T>
where T : Component
{
AncestorsEnumerable parent;
public OfComponentEnumerable(ref AncestorsEnumerable parent)
{
this.parent = parent;
}
public OfComponentEnumerator<T> GetEnumerator()
{
return new OfComponentEnumerator<T>(ref parent);
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#region LINQ
public void ForEach(Action<T> action)
{
var e = this.GetEnumerator();
while (e.MoveNext())
{
action(e.Current);
}
}
public T First()
{
var e = this.GetEnumerator();
if (e.MoveNext())
{
return e.Current;
}
else
{
throw new InvalidOperationException("시퀀스가 비어 있습니다.");
}
}
public T FirstOrDefault()
{
var e = this.GetEnumerator();
return (e.MoveNext())
? e.Current
: null;
}
public T[] ToArray()
{
var array = new T[4];
var len = ToArrayNonAlloc(ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc(ref T[] array)
{
var index = 0;
var e = this.GetEnumerator();
while (e.MoveNext())
{
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = e.Current;
}
return index;
}
#endregion
}
public struct OfComponentEnumerator<T> : IEnumerator<T>
where T : Component
{
Enumerator enumerator; // 열거자는 변경 가능합니다.
T current;
#if UNITY_EDITOR
static List<T> componentCache = new List<T>(); // UNITY_EDITOR에서 할당 없음을 위함
#endif
public OfComponentEnumerator(ref AncestorsEnumerable parent)
{
this.enumerator = parent.GetEnumerator();
this.current = default(T);
}
public bool MoveNext()
{
while (enumerator.MoveNext())
{
#if UNITY_EDITOR
enumerator.Current.GetComponents<T>(componentCache);
if (componentCache.Count != 0)
{
current = componentCache[0];
componentCache.Clear();
return true;
}
#else
var component = enumerator.Current.GetComponent<T>();
if (component != null)
{
current = component;
return true;
}
#endif
}
return false;
}
public T Current { get { return current; } }
object IEnumerator.Current { get { return current; } }
public void Dispose() { }
public void Reset() { throw new NotSupportedException(); }
}
}
public struct DescendantsEnumerable : IEnumerable<GameObject>
{
static readonly Func<Transform, bool> alwaysTrue = _ => true;
readonly GameObject origin;
readonly bool withSelf;
readonly Func<Transform, bool> descendIntoChildren;
public DescendantsEnumerable(GameObject origin, bool withSelf, Func<Transform, bool> descendIntoChildren)
{
this.origin = origin;
this.withSelf = withSelf;
this.descendIntoChildren = descendIntoChildren ?? alwaysTrue;
}
/// <summary>소스 컬렉션에서 지정된 컴포넌트의 컬렉션을 반환합니다.</summary>
public OfComponentEnumerable<T> OfComponent<T>()
where T : Component
{
return new OfComponentEnumerable<T>(ref this);
}
/// <summary>소스 컬렉션의 모든 GameObject를 안전하게(null 확인) 파괴합니다.</summary>
/// <param name="useDestroyImmediate">에디터 모드인 경우 true이거나 !Application.isPlaying을 전달해야 합니다.</param>
public void Destroy(bool useDestroyImmediate = false)
{
var e = GetEnumerator();
while (e.MoveNext())
{
e.Current.Destroy(useDestroyImmediate, false);
}
}
/// <summary>소스 컬렉션의 모든 GameObject를 안전하게(null 확인) 파괴합니다.</summary>
/// <param name="useDestroyImmediate">에디터 모드인 경우 true이거나 !Application.isPlaying을 전달해야 합니다.</param>
public void Destroy(Func<GameObject, bool> predicate, bool useDestroyImmediate = false)
{
var e = GetEnumerator();
while (e.MoveNext())
{
var item = e.Current;
if (predicate(item))
{
item.Destroy(useDestroyImmediate, false);
}
}
}
public Enumerator GetEnumerator()
{
// GetEnumerator 시점에만 GameObject가 파괴되었는지 확인합니다.
if (origin == null)
{
return new Enumerator(null, withSelf, false, null, descendIntoChildren);
}
InternalUnsafeRefStack refStack;
if (InternalUnsafeRefStack.RefStackPool.Count != 0)
{
refStack = InternalUnsafeRefStack.RefStackPool.Dequeue();
refStack.Reset();
}
else
{
refStack = new InternalUnsafeRefStack(6);
}
return new Enumerator(origin.transform, withSelf, true, refStack, descendIntoChildren);
}
IEnumerator<GameObject> IEnumerable<GameObject>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#region LINQ
void ResizeArray<T>(ref int index, ref T[] array)
{
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
}
void DescendantsCore(ref Transform transform, ref Action<GameObject> action)
{
if (!descendIntoChildren(transform)) return;
var childCount = transform.childCount;
for (int i = 0; i < childCount; i++)
{
var child = transform.GetChild(i);
action(child.gameObject);
DescendantsCore(ref child, ref action);
}
}
void DescendantsCore(ref Transform transform, ref int index, ref GameObject[] array)
{
if (!descendIntoChildren(transform)) return;
var childCount = transform.childCount;
for (int i = 0; i < childCount; i++)
{
var child = transform.GetChild(i);
ResizeArray(ref index, ref array);
array[index++] = child.gameObject;
DescendantsCore(ref child, ref index, ref array);
}
}
void DescendantsCore(ref Func<GameObject, bool> filter, ref Transform transform, ref int index, ref GameObject[] array)
{
if (!descendIntoChildren(transform)) return;
var childCount = transform.childCount;
for (int i = 0; i < childCount; i++)
{
var child = transform.GetChild(i);
var childGameObject = child.gameObject;
if (filter(childGameObject))
{
ResizeArray(ref index, ref array);
array[index++] = childGameObject;
}
DescendantsCore(ref filter, ref child, ref index, ref array);
}
}
void DescendantsCore<T>(ref Func<GameObject, T> selector, ref Transform transform, ref int index, ref T[] array)
{
if (!descendIntoChildren(transform)) return;
var childCount = transform.childCount;
for (int i = 0; i < childCount; i++)
{
var child = transform.GetChild(i);
ResizeArray(ref index, ref array);
array[index++] = selector(child.gameObject);
DescendantsCore(ref selector, ref child, ref index, ref array);
}
}
void DescendantsCore<T>(ref Func<GameObject, bool> filter, ref Func<GameObject, T> selector, ref Transform transform, ref int index, ref T[] array)
{
if (!descendIntoChildren(transform)) return;
var childCount = transform.childCount;
for (int i = 0; i < childCount; i++)
{
var child = transform.GetChild(i);
var childGameObject = child.gameObject;
if (filter(childGameObject))
{
ResizeArray(ref index, ref array);
array[index++] = selector(childGameObject);
}
DescendantsCore(ref filter, ref selector, ref child, ref index, ref array);
}
}
void DescendantsCore<TState, T>(ref Func<GameObject, TState> let, ref Func<TState, bool> filter, ref Func<TState, T> selector, ref Transform transform, ref int index, ref T[] array)
{
if (!descendIntoChildren(transform)) return;
var childCount = transform.childCount;
for (int i = 0; i < childCount; i++)
{
var child = transform.GetChild(i);
var state = let(child.gameObject);
if (filter(state))
{
ResizeArray(ref index, ref array);
array[index++] = selector(state);
}
DescendantsCore(ref let, ref filter, ref selector, ref child, ref index, ref array);
}
}
/// <summary>성능 최적화를 위해 내부 반복기를 사용합니다.</summary>
/// <param name="action"></param>
public void ForEach(Action<GameObject> action)
{
if (withSelf)
{
action(origin);
}
var originTransform = origin.transform;
DescendantsCore(ref originTransform, ref action);
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc(ref GameObject[] array)
{
var index = 0;
if (withSelf)
{
ResizeArray(ref index, ref array);
array[index++] = origin;
}
var originTransform = origin.transform;
DescendantsCore(ref originTransform, ref index, ref array);
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc(Func<GameObject, bool> filter, ref GameObject[] array)
{
var index = 0;
if (withSelf && filter(origin))
{
ResizeArray(ref index, ref array);
array[index++] = origin;
}
var originTransform = origin.transform;
DescendantsCore(ref filter, ref originTransform, ref index, ref array);
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc<T>(Func<GameObject, T> selector, ref T[] array)
{
var index = 0;
if (withSelf)
{
ResizeArray(ref index, ref array);
array[index++] = selector(origin);
}
var originTransform = origin.transform;
DescendantsCore(ref selector, ref originTransform, ref index, ref array);
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc<T>(Func<GameObject, bool> filter, Func<GameObject, T> selector, ref T[] array)
{
var index = 0;
if (withSelf && filter(origin))
{
ResizeArray(ref index, ref array);
array[index++] = selector(origin);
}
var originTransform = origin.transform;
DescendantsCore(ref filter, ref selector, ref originTransform, ref index, ref array);
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc<TState, T>(Func<GameObject, TState> let, Func<TState, bool> filter, Func<TState, T> selector, ref T[] array)
{
var index = 0;
if (withSelf)
{
var state = let(origin);
if (filter(state))
{
ResizeArray(ref index, ref array);
array[index++] = selector(state);
}
}
var originTransform = origin.transform;
DescendantsCore(ref let, ref filter, ref selector, ref originTransform, ref index, ref array);
return index;
}
public GameObject[] ToArray()
{
var array = new GameObject[4];
var len = ToArrayNonAlloc(ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public GameObject[] ToArray(Func<GameObject, bool> filter)
{
var array = new GameObject[4];
var len = ToArrayNonAlloc(filter, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public T[] ToArray<T>(Func<GameObject, T> selector)
{
var array = new T[4];
var len = ToArrayNonAlloc<T>(selector, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public T[] ToArray<T>(Func<GameObject, bool> filter, Func<GameObject, T> selector)
{
var array = new T[4];
var len = ToArrayNonAlloc(filter, selector, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public T[] ToArray<TState, T>(Func<GameObject, TState> let, Func<TState, bool> filter, Func<TState, T> selector)
{
var array = new T[4];
var len = ToArrayNonAlloc(let, filter, selector, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public GameObject First()
{
var e = this.GetEnumerator();
try
{
if (e.MoveNext())
{
return e.Current;
}
else
{
throw new InvalidOperationException("시퀀스가 비어 있습니다.");
}
}
finally
{
e.Dispose();
}
}
public GameObject FirstOrDefault()
{
var e = this.GetEnumerator();
try
{
return (e.MoveNext())
? e.Current
: null;
}
finally
{
e.Dispose();
}
}
#endregion
internal class InternalUnsafeRefStack
{
public static Queue<InternalUnsafeRefStack> RefStackPool = new Queue<InternalUnsafeRefStack>();
public int size = 0;
public Enumerator[] array; // Pop = this.array[--size];
public InternalUnsafeRefStack(int initialStackDepth)
{
array = new GameObjectExtensions.DescendantsEnumerable.Enumerator[initialStackDepth];
}
public void Push(ref Enumerator e)
{
if (size == array.Length)
{
Array.Resize(ref array, array.Length * 2);
}
array[size++] = e;
}
public void Reset()
{
size = 0;
}
}
public struct Enumerator : IEnumerator<GameObject>
{
readonly int childCount; // childCount는 GetEnumerator가 호출될 때 고정됩니다.
readonly Transform originTransform;
bool canRun;
bool withSelf;
int currentIndex;
GameObject current;
InternalUnsafeRefStack sharedStack;
Func<Transform, bool> descendIntoChildren;
internal Enumerator(Transform originTransform, bool withSelf, bool canRun, InternalUnsafeRefStack sharedStack, Func<Transform, bool> descendIntoChildren)
{
this.originTransform = originTransform;
this.withSelf = withSelf;
this.childCount = canRun ? originTransform.childCount : 0;
this.currentIndex = -1;
this.canRun = canRun;
this.current = null;
this.sharedStack = sharedStack;
this.descendIntoChildren = descendIntoChildren;
}
public bool MoveNext()
{
if (!canRun) return false;
while (sharedStack.size != 0)
{
if (sharedStack.array[sharedStack.size - 1].MoveNextCore(true, out current))
{
return true;
}
}
if (!withSelf && !descendIntoChildren(originTransform))
{
// 재사용
canRun = false;
InternalUnsafeRefStack.RefStackPool.Enqueue(sharedStack);
return false;
}
if (MoveNextCore(false, out current))
{
return true;
}
else
{
// 재사용
canRun = false;
InternalUnsafeRefStack.RefStackPool.Enqueue(sharedStack);
return false;
}
}
bool MoveNextCore(bool peek, out GameObject current)
{
if (withSelf)
{
current = originTransform.gameObject;
withSelf = false;
return true;
}
++currentIndex;
if (currentIndex < childCount)
{
var item = originTransform.GetChild(currentIndex);
if (descendIntoChildren(item))
{
var childEnumerator = new Enumerator(item, true, true, sharedStack, descendIntoChildren);
sharedStack.Push(ref childEnumerator);
return sharedStack.array[sharedStack.size - 1].MoveNextCore(true, out current);
}
else
{
current = item.gameObject;
return true;
}
}
if (peek)
{
sharedStack.size--; // Pop
}
current = null;
return false;
}
public GameObject Current { get { return current; } }
object IEnumerator.Current { get { return current; } }
public void Dispose()
{
if (canRun)
{
canRun = false;
InternalUnsafeRefStack.RefStackPool.Enqueue(sharedStack);
}
}
public void Reset() { throw new NotSupportedException(); }
}
public struct OfComponentEnumerable<T> : IEnumerable<T>
where T : Component
{
DescendantsEnumerable parent;
public OfComponentEnumerable(ref DescendantsEnumerable parent)
{
this.parent = parent;
}
public OfComponentEnumerator<T> GetEnumerator()
{
return new OfComponentEnumerator<T>(ref parent);
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#region LINQ
public T First()
{
var e = this.GetEnumerator();
try
{
if (e.MoveNext())
{
return e.Current;
}
else
{
throw new InvalidOperationException("시퀀스가 비어 있습니다.");
}
}
finally
{
e.Dispose();
}
}
public T FirstOrDefault()
{
var e = this.GetEnumerator();
try
{
return (e.MoveNext())
? e.Current
: null;
}
finally
{
e.Dispose();
}
}
/// <summary>성능 최적화를 위해 내부 반복기를 사용합니다.</summary>
public void ForEach(Action<T> action)
{
if (parent.withSelf)
{
T component = default(T);
#if UNITY_EDITOR
parent.origin.GetComponents<T>(componentCache);
if (componentCache.Count != 0)
{
component = componentCache[0];
componentCache.Clear();
}
#else
component = parent.origin.GetComponent<T>();
#endif
if (component != null)
{
action(component);
}
}
var originTransform = parent.origin.transform;
OfComponentDescendantsCore(ref originTransform, ref action);
}
public T[] ToArray()
{
var array = new T[4];
var len = ToArrayNonAlloc(ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
#if UNITY_EDITOR
static List<T> componentCache = new List<T>(); // UNITY_EDITOR에서 할당 없음을 위함
#endif
void OfComponentDescendantsCore(ref Transform transform, ref Action<T> action)
{
if (!parent.descendIntoChildren(transform)) return;
var childCount = transform.childCount;
for (int i = 0; i < childCount; i++)
{
var child = transform.GetChild(i);
T component = default(T);
#if UNITY_EDITOR
child.GetComponents<T>(componentCache);
if (componentCache.Count != 0)
{
component = componentCache[0];
componentCache.Clear();
}
#else
component = child.GetComponent<T>();
#endif
if (component != null)
{
action(component);
}
OfComponentDescendantsCore(ref child, ref action);
}
}
void OfComponentDescendantsCore(ref Transform transform, ref int index, ref T[] array)
{
if (!parent.descendIntoChildren(transform)) return;
var childCount = transform.childCount;
for (int i = 0; i < childCount; i++)
{
var child = transform.GetChild(i);
T component = default(T);
#if UNITY_EDITOR
child.GetComponents<T>(componentCache);
if (componentCache.Count != 0)
{
component = componentCache[0];
componentCache.Clear();
}
#else
component = child.GetComponent<T>();
#endif
if (component != null)
{
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = component;
}
OfComponentDescendantsCore(ref child, ref index, ref array);
}
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc(ref T[] array)
{
var index = 0;
if (parent.withSelf)
{
T component = default(T);
#if UNITY_EDITOR
parent.origin.GetComponents<T>(componentCache);
if (componentCache.Count != 0)
{
component = componentCache[0];
componentCache.Clear();
}
#else
component = parent.origin.GetComponent<T>();
#endif
if (component != null)
{
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = component;
}
}
var originTransform = parent.origin.transform;
OfComponentDescendantsCore(ref originTransform, ref index, ref array);
return index;
}
#endregion
}
public struct OfComponentEnumerator<T> : IEnumerator<T>
where T : Component
{
Enumerator enumerator; // 열거자는 변경 가능합니다.
T current;
#if UNITY_EDITOR
static List<T> componentCache = new List<T>(); // UNITY_EDITOR에서 할당 없음을 위함
#endif
public OfComponentEnumerator(ref DescendantsEnumerable parent)
{
this.enumerator = parent.GetEnumerator();
this.current = default(T);
}
public bool MoveNext()
{
while (enumerator.MoveNext())
{
#if UNITY_EDITOR
enumerator.Current.GetComponents<T>(componentCache);
if (componentCache.Count != 0)
{
current = componentCache[0];
componentCache.Clear();
return true;
}
#else
var component = enumerator.Current.GetComponent<T>();
if (component != null)
{
current = component;
return true;
}
#endif
}
return false;
}
public T Current { get { return current; } }
object IEnumerator.Current { get { return current; } }
public void Dispose()
{
enumerator.Dispose();
}
public void Reset() { throw new NotSupportedException(); }
}
}
public struct BeforeSelfEnumerable : IEnumerable<GameObject>
{
readonly GameObject origin;
readonly bool withSelf;
public BeforeSelfEnumerable(GameObject origin, bool withSelf)
{
this.origin = origin;
this.withSelf = withSelf;
}
/// <summary>소스 컬렉션에서 지정된 컴포넌트의 컬렉션을 반환합니다.</summary>
public OfComponentEnumerable<T> OfComponent<T>()
where T : Component
{
return new OfComponentEnumerable<T>(ref this);
}
/// <summary>소스 컬렉션의 모든 GameObject를 안전하게(null 확인) 파괴합니다.</summary>
/// <param name="useDestroyImmediate">에디터 모드인 경우 true이거나 !Application.isPlaying을 전달해야 합니다.</param>
public void Destroy(bool useDestroyImmediate = false)
{
var e = GetEnumerator();
while (e.MoveNext())
{
e.Current.Destroy(useDestroyImmediate, false);
}
}
/// <summary>소스 컬렉션의 모든 GameObject를 안전하게(null 확인) 파괴합니다.</summary>
/// <param name="useDestroyImmediate">에디터 모드인 경우 true이거나 !Application.isPlaying을 전달해야 합니다.</param>
public void Destroy(Func<GameObject, bool> predicate, bool useDestroyImmediate = false)
{
var e = GetEnumerator();
while (e.MoveNext())
{
var item = e.Current;
if (predicate(item))
{
item.Destroy(useDestroyImmediate, false);
}
}
}
public Enumerator GetEnumerator()
{
// GetEnumerator 시점에만 GameObject가 파괴되었는지 확인합니다.
return (origin == null)
? new Enumerator(null, withSelf, false)
: new Enumerator(origin.transform, withSelf, true);
}
IEnumerator<GameObject> IEnumerable<GameObject>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#region LINQ
public void ForEach(Action<GameObject> action)
{
var e = this.GetEnumerator();
while (e.MoveNext())
{
action(e.Current);
}
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc(ref GameObject[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = item;
}
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc(Func<GameObject, bool> filter, ref GameObject[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
if (!filter(item)) continue;
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = item;
}
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc<T>(Func<GameObject, T> selector, ref T[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = selector(item);
}
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc<T>(Func<GameObject, bool> filter, Func<GameObject, T> selector, ref T[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
if (!filter(item)) continue;
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = selector(item);
}
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc<TState, T>(Func<GameObject, TState> let, Func<TState, bool> filter, Func<TState, T> selector, ref T[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
var state = let(item);
if (!filter(state)) continue;
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = selector(state);
}
return index;
}
public GameObject[] ToArray()
{
var array = new GameObject[4];
var len = ToArrayNonAlloc(ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public GameObject[] ToArray(Func<GameObject, bool> filter)
{
var array = new GameObject[4];
var len = ToArrayNonAlloc(filter, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public T[] ToArray<T>(Func<GameObject, T> selector)
{
var array = new T[4];
var len = ToArrayNonAlloc<T>(selector, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public T[] ToArray<T>(Func<GameObject, bool> filter, Func<GameObject, T> selector)
{
var array = new T[4];
var len = ToArrayNonAlloc(filter, selector, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public T[] ToArray<TState, T>(Func<GameObject, TState> let, Func<TState, bool> filter, Func<TState, T> selector)
{
var array = new T[4];
var len = ToArrayNonAlloc(let, filter, selector, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public GameObject First()
{
var e = this.GetEnumerator();
if (e.MoveNext())
{
return e.Current;
}
else
{
throw new InvalidOperationException("시퀀스가 비어 있습니다.");
}
}
public GameObject FirstOrDefault()
{
var e = this.GetEnumerator();
return (e.MoveNext())
? e.Current
: null;
}
#endregion
public struct Enumerator : IEnumerator<GameObject>
{
readonly int childCount; // childCount는 GetEnumerator가 호출될 때 고정됩니다.
readonly Transform originTransform;
bool canRun;
bool withSelf;
int currentIndex;
GameObject current;
Transform parent;
internal Enumerator(Transform originTransform, bool withSelf, bool canRun)
{
this.originTransform = originTransform;
this.withSelf = withSelf;
this.currentIndex = -1;
this.canRun = canRun;
this.current = null;
this.parent = originTransform.parent;
this.childCount = (parent != null) ? parent.childCount : 0;
}
public bool MoveNext()
{
if (!canRun) return false;
if (parent == null) goto RETURN_SELF;
currentIndex++;
if (currentIndex < childCount)
{
var item = parent.GetChild(currentIndex);
if (item == originTransform)
{
goto RETURN_SELF;
}
current = item.gameObject;
return true;
}
RETURN_SELF:
if (withSelf)
{
current = originTransform.gameObject;
withSelf = false;
canRun = false; // 자신에게 도달했으므로 실행 완료.
return true;
}
return false;
}
public GameObject Current { get { return current; } }
object IEnumerator.Current { get { return current; } }
public void Dispose() { }
public void Reset() { throw new NotSupportedException(); }
}
public struct OfComponentEnumerable<T> : IEnumerable<T>
where T : Component
{
BeforeSelfEnumerable parent;
public OfComponentEnumerable(ref BeforeSelfEnumerable parent)
{
this.parent = parent;
}
public OfComponentEnumerator<T> GetEnumerator()
{
return new OfComponentEnumerator<T>(ref parent);
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#region LINQ
public void ForEach(Action<T> action)
{
var e = this.GetEnumerator();
while (e.MoveNext())
{
action(e.Current);
}
}
public T First()
{
var e = this.GetEnumerator();
if (e.MoveNext())
{
return e.Current;
}
else
{
throw new InvalidOperationException("시퀀스가 비어 있습니다.");
}
}
public T FirstOrDefault()
{
var e = this.GetEnumerator();
return (e.MoveNext())
? e.Current
: null;
}
public T[] ToArray()
{
var array = new T[4];
var len = ToArrayNonAlloc(ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc(ref T[] array)
{
var index = 0;
var e = this.GetEnumerator();
while (e.MoveNext())
{
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = e.Current;
}
return index;
}
#endregion
}
public struct OfComponentEnumerator<T> : IEnumerator<T>
where T : Component
{
Enumerator enumerator; // 열거자는 변경 가능합니다.
T current;
#if UNITY_EDITOR
static List<T> componentCache = new List<T>(); // UNITY_EDITOR에서 할당 없음을 위함
#endif
public OfComponentEnumerator(ref BeforeSelfEnumerable parent)
{
this.enumerator = parent.GetEnumerator();
this.current = default(T);
}
public bool MoveNext()
{
while (enumerator.MoveNext())
{
#if UNITY_EDITOR
enumerator.Current.GetComponents<T>(componentCache);
if (componentCache.Count != 0)
{
current = componentCache[0];
componentCache.Clear();
return true;
}
#else
var component = enumerator.Current.GetComponent<T>();
if (component != null)
{
current = component;
return true;
}
#endif
}
return false;
}
public T Current { get { return current; } }
object IEnumerator.Current { get { return current; } }
public void Dispose() { }
public void Reset() { throw new NotSupportedException(); }
}
}
public struct AfterSelfEnumerable : IEnumerable<GameObject>
{
readonly GameObject origin;
readonly bool withSelf;
public AfterSelfEnumerable(GameObject origin, bool withSelf)
{
this.origin = origin;
this.withSelf = withSelf;
}
/// <summary>소스 컬렉션에서 지정된 컴포넌트의 컬렉션을 반환합니다.</summary>
public OfComponentEnumerable<T> OfComponent<T>()
where T : Component
{
return new OfComponentEnumerable<T>(ref this);
}
/// <summary>소스 컬렉션의 모든 GameObject를 안전하게(null 확인) 파괴합니다.</summary>
/// <param name="useDestroyImmediate">에디터 모드인 경우 true이거나 !Application.isPlaying을 전달해야 합니다.</param>
public void Destroy(bool useDestroyImmediate = false)
{
var e = GetEnumerator();
while (e.MoveNext())
{
e.Current.Destroy(useDestroyImmediate, false);
}
}
/// <summary>소스 컬렉션의 모든 GameObject를 안전하게(null 확인) 파괴합니다.</summary>
/// <param name="useDestroyImmediate">에디터 모드인 경우 true이거나 !Application.isPlaying을 전달해야 합니다.</param>
public void Destroy(Func<GameObject, bool> predicate, bool useDestroyImmediate = false)
{
var e = GetEnumerator();
while (e.MoveNext())
{
var item = e.Current;
if (predicate(item))
{
item.Destroy(useDestroyImmediate, false);
}
}
}
public Enumerator GetEnumerator()
{
// GetEnumerator 시점에만 GameObject가 파괴되었는지 확인합니다.
return (origin == null)
? new Enumerator(null, withSelf, false)
: new Enumerator(origin.transform, withSelf, true);
}
IEnumerator<GameObject> IEnumerable<GameObject>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#region LINQ
public void ForEach(Action<GameObject> action)
{
var e = this.GetEnumerator();
while (e.MoveNext())
{
action(e.Current);
}
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc(ref GameObject[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = item;
}
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc(Func<GameObject, bool> filter, ref GameObject[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
if (!filter(item)) continue;
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = item;
}
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc<T>(Func<GameObject, T> selector, ref T[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = selector(item);
}
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc<T>(Func<GameObject, bool> filter, Func<GameObject, T> selector, ref T[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
if (!filter(item)) continue;
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = selector(item);
}
return index;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc<TState, T>(Func<GameObject, TState> let, Func<TState, bool> filter, Func<TState, T> selector, ref T[] array)
{
var index = 0;
var e = this.GetEnumerator(); // Dispose를 호출할 필요가 없습니다.
while (e.MoveNext())
{
var item = e.Current;
var state = let(item);
if (!filter(state)) continue;
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = selector(state);
}
return index;
}
public GameObject[] ToArray()
{
var array = new GameObject[4];
var len = ToArrayNonAlloc(ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public GameObject[] ToArray(Func<GameObject, bool> filter)
{
var array = new GameObject[4];
var len = ToArrayNonAlloc(filter, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public T[] ToArray<T>(Func<GameObject, T> selector)
{
var array = new T[4];
var len = ToArrayNonAlloc<T>(selector, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public T[] ToArray<T>(Func<GameObject, bool> filter, Func<GameObject, T> selector)
{
var array = new T[4];
var len = ToArrayNonAlloc(filter, selector, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public T[] ToArray<TState, T>(Func<GameObject, TState> let, Func<TState, bool> filter, Func<TState, T> selector)
{
var array = new T[4];
var len = ToArrayNonAlloc(let, filter, selector, ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
public GameObject First()
{
var e = this.GetEnumerator();
if (e.MoveNext())
{
return e.Current;
}
else
{
throw new InvalidOperationException("시퀀스가 비어 있습니다.");
}
}
public GameObject FirstOrDefault()
{
var e = this.GetEnumerator();
return (e.MoveNext())
? e.Current
: null;
}
#endregion
public struct Enumerator : IEnumerator<GameObject>
{
readonly int childCount; // childCount는 GetEnumerator가 호출될 때 고정됩니다.
readonly Transform originTransform;
readonly bool canRun;
bool withSelf;
int currentIndex;
GameObject current;
Transform parent;
internal Enumerator(Transform originTransform, bool withSelf, bool canRun)
{
this.originTransform = originTransform;
this.withSelf = withSelf;
this.currentIndex = (originTransform != null) ? originTransform.GetSiblingIndex() + 1 : 0;
this.canRun = canRun;
this.current = null;
this.parent = originTransform.parent;
this.childCount = (parent != null) ? parent.childCount : 0;
}
public bool MoveNext()
{
if (!canRun) return false;
if (withSelf)
{
current = originTransform.gameObject;
withSelf = false;
return true;
}
if (currentIndex < childCount)
{
current = parent.GetChild(currentIndex).gameObject;
currentIndex++;
return true;
}
return false;
}
public GameObject Current { get { return current; } }
object IEnumerator.Current { get { return current; } }
public void Dispose() { }
public void Reset() { throw new NotSupportedException(); }
}
public struct OfComponentEnumerable<T> : IEnumerable<T>
where T : Component
{
AfterSelfEnumerable parent;
public OfComponentEnumerable(ref AfterSelfEnumerable parent)
{
this.parent = parent;
}
public OfComponentEnumerator<T> GetEnumerator()
{
return new OfComponentEnumerator<T>(ref this.parent);
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#region LINQ
public void ForEach(Action<T> action)
{
var e = this.GetEnumerator();
while (e.MoveNext())
{
action(e.Current);
}
}
public T First()
{
var e = this.GetEnumerator();
if (e.MoveNext())
{
return e.Current;
}
else
{
throw new InvalidOperationException("시퀀스가 비어 있습니다.");
}
}
public T FirstOrDefault()
{
var e = this.GetEnumerator();
return (e.MoveNext())
? e.Current
: null;
}
public T[] ToArray()
{
var array = new T[4];
var len = ToArrayNonAlloc(ref array);
if (array.Length != len)
{
Array.Resize(ref array, len);
}
return array;
}
/// <summary>요소를 버퍼에 저장하고 크기를 반환합니다. 배열은 자동으로 확장됩니다.</summary>
public int ToArrayNonAlloc(ref T[] array)
{
var index = 0;
var e = this.GetEnumerator();
while (e.MoveNext())
{
if (array.Length == index)
{
var newSize = (index == 0) ? 4 : index * 2;
Array.Resize(ref array, newSize);
}
array[index++] = e.Current;
}
return index;
}
#endregion
}
public struct OfComponentEnumerator<T> : IEnumerator<T>
where T : Component
{
Enumerator enumerator; // 열거자는 변경 가능합니다.
T current;
#if UNITY_EDITOR
static List<T> componentCache = new List<T>(); // UNITY_EDITOR에서 할당 없음을 위함
#endif
public OfComponentEnumerator(ref AfterSelfEnumerable parent)
{
this.enumerator = parent.GetEnumerator();
this.current = default(T);
}
public bool MoveNext()
{
while (enumerator.MoveNext())
{
#if UNITY_EDITOR
enumerator.Current.GetComponents<T>(componentCache);
if (componentCache.Count != 0)
{
current = componentCache[0];
componentCache.Clear();
return true;
}
#else
var component = enumerator.Current.GetComponent<T>();
if (component != null)
{
current = component;
return true;
}
#endif
}
return false;
}
public T Current { get { return current; } }
object IEnumerator.Current { get { return current; } }
public void Dispose() { }
public void Reset() { throw new NotSupportedException(); }
}
}
}
}