#nullable enable using System; using System.Collections.Generic; using UnityEngine; namespace UVC.Linq { /// /// 복제된 자식 GameObject의 localPosition/Scale/Rotation 설정 타입입니다. /// public enum TransformCloneType { /// 원본과 동일하게 설정합니다. AddChild 메소드의 기본값입니다. KeepOriginal, /// 부모와 동일하게 설정합니다. FollowParent, /// Position = 영벡터, Scale = 단위벡터, Rotation = 항등 회전으로 설정합니다. Origin, /// Position/Scale/Rotation을 그대로 유지합니다. DoNothing } /// /// 이동된 자식 GameObject의 localPosition/Scale/Rotation 설정 타입입니다. /// public enum TransformMoveType { /// 부모와 동일하게 설정합니다. FollowParent, /// Position = 영벡터, Scale = 단위벡터, Rotation = 항등 회전으로 설정합니다. Origin, /// Position/Scale/Rotation을 그대로 유지합니다. DoNothing } public static partial class GameObjectExtensions { static UnityEngine.GameObject GetGameObject(T obj) where T : UnityEngine.Object { var gameObject = obj as GameObject; if (gameObject == null) { var component = obj as Component; if (component == null) { return null; } gameObject = component.gameObject; } return gameObject; } #region Add /// /// GameObject/Component를 이 GameObject의 자식으로 추가합니다. 대상이 복제됩니다. /// /// 부모 GameObject입니다. /// 복제할 대상입니다. /// 복제된 자식 GameObject의 localPosition/Scale/Rotation 설정 타입을 선택합니다. /// 자식 GameObject의 활성/비활성 상태를 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 이름을 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 레이어를 부모와 동일하게 설정할지 여부입니다. public static T Add(this GameObject parent, T childOriginal, TransformCloneType cloneType = TransformCloneType.KeepOriginal, bool? setActive = null, string? specifiedName = null, bool setLayer = false) where T : UnityEngine.Object { if (parent == null) throw new ArgumentNullException("parent"); if (childOriginal == null) throw new ArgumentNullException("childOriginal"); var child = UnityEngine.Object.Instantiate(childOriginal); var childGameObject = GetGameObject(child); // uGUI의 경우 SetParent(parent, false)를 사용해야 합니다 var childTransform = childGameObject.transform; #if !(UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5) var rectTransform = childTransform as RectTransform; if (rectTransform != null) { rectTransform.SetParent(parent.transform, worldPositionStays: false); } else { #endif var parentTransform = parent.transform; childTransform.parent = parentTransform; switch (cloneType) { case TransformCloneType.FollowParent: childTransform.localPosition = parentTransform.localPosition; childTransform.localScale = parentTransform.localScale; childTransform.localRotation = parentTransform.localRotation; break; case TransformCloneType.Origin: childTransform.localPosition = Vector3.zero; childTransform.localScale = Vector3.one; childTransform.localRotation = Quaternion.identity; break; case TransformCloneType.KeepOriginal: var co = GetGameObject(childOriginal); var childOriginalTransform = co.transform; childTransform.localPosition = childOriginalTransform.localPosition; childTransform.localScale = childOriginalTransform.localScale; childTransform.localRotation = childOriginalTransform.localRotation; break; case TransformCloneType.DoNothing: default: break; } #if !(UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5) } #endif if (setLayer) { childGameObject.layer = parent.layer; } if (setActive != null) { childGameObject.SetActive(setActive.Value); } if (specifiedName != null) { child.name = specifiedName; } return child; } /// /// GameObject/Component를 이 GameObject의 자식으로 추가합니다. 대상이 복제됩니다. /// /// 부모 GameObject입니다. /// 복제할 대상입니다. /// 복제된 자식 GameObject의 localPosition/Scale/Rotation 설정 타입을 선택합니다. /// 자식 GameObject의 활성/비활성 상태를 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 이름을 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 레이어를 부모와 동일하게 설정할지 여부입니다. public static T[] AddRange(this GameObject parent, IEnumerable childOriginals, TransformCloneType cloneType = TransformCloneType.KeepOriginal, bool? setActive = null, string specifiedName = null, bool setLayer = false) where T : UnityEngine.Object { if (parent == null) throw new ArgumentNullException("parent"); if (childOriginals == null) throw new ArgumentNullException("childOriginals"); // 반복 최적화 { var array = childOriginals as T[]; if (array != null) { var result = new T[array.Length]; for (int i = 0; i < array.Length; i++) { var child = Add(parent, array[i], cloneType, setActive, specifiedName, setLayer); result[i] = child; } return result; } } { var iterList = childOriginals as IList; if (iterList != null) { var result = new T[iterList.Count]; for (int i = 0; i < iterList.Count; i++) { var child = Add(parent, iterList[i], cloneType, setActive, specifiedName, setLayer); result[i] = child; } return result; } } { var result = new List(); foreach (var childOriginal in childOriginals) { var child = Add(parent, childOriginal, cloneType, setActive, specifiedName, setLayer); result.Add(child); } return result.ToArray(); } } /// /// GameObject/Component를 이 GameObject의 첫 번째 자식으로 추가합니다. 대상이 복제됩니다. /// /// 부모 GameObject입니다. /// 복제할 대상입니다. /// 복제된 자식 GameObject의 localPosition/Scale/Rotation 설정 타입을 선택합니다. /// 자식 GameObject의 활성/비활성 상태를 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 이름을 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 레이어를 부모와 동일하게 설정할지 여부입니다. public static T AddFirst(this GameObject parent, T childOriginal, TransformCloneType cloneType = TransformCloneType.KeepOriginal, bool? setActive = null, string specifiedName = null, bool setLayer = false) where T : UnityEngine.Object { var child = Add(parent, childOriginal, cloneType, setActive, specifiedName, setLayer); var go = GetGameObject(child); if (go == null) return child; go.transform.SetAsFirstSibling(); return child; } /// /// GameObject/Component를 이 GameObject의 첫 번째 자식으로 추가합니다. 대상이 복제됩니다. /// /// 부모 GameObject입니다. /// 복제할 대상입니다. /// 복제된 자식 GameObject의 localPosition/Scale/Rotation 설정 타입을 선택합니다. /// 자식 GameObject의 활성/비활성 상태를 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 이름을 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 레이어를 부모와 동일하게 설정할지 여부입니다. public static T[] AddFirstRange(this GameObject parent, IEnumerable childOriginals, TransformCloneType cloneType = TransformCloneType.KeepOriginal, bool? setActive = null, string specifiedName = null, bool setLayer = false) where T : UnityEngine.Object { var child = AddRange(parent, childOriginals, cloneType, setActive, specifiedName, setLayer); for (int i = child.Length - 1; i >= 0; i--) { var go = GetGameObject(child[i]); if (go == null) continue; go.transform.SetAsFirstSibling(); } return child; } /// /// GameObject/Component를 이 GameObject 앞에 추가합니다. 대상이 복제됩니다. /// /// 부모 GameObject입니다. /// 복제할 대상입니다. /// 복제된 자식 GameObject의 localPosition/Scale/Rotation 설정 타입을 선택합니다. /// 자식 GameObject의 활성/비활성 상태를 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 이름을 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 레이어를 부모와 동일하게 설정할지 여부입니다. public static T AddBeforeSelf(this GameObject parent, T childOriginal, TransformCloneType cloneType = TransformCloneType.KeepOriginal, bool? setActive = null, string specifiedName = null, bool setLayer = false) where T : UnityEngine.Object { var root = parent.Parent(); if (root == null) throw new InvalidOperationException("The parent root is null"); var sibilingIndex = parent.transform.GetSiblingIndex(); var child = Add(root, childOriginal, cloneType, setActive, specifiedName, setLayer); var go = GetGameObject(child); if (go == null) return child; go.transform.SetSiblingIndex(sibilingIndex); return child; } /// /// GameObject/Component를 이 GameObject 앞에 추가합니다. 대상이 복제됩니다. /// /// 부모 GameObject입니다. /// 복제할 대상입니다. /// 복제된 자식 GameObject의 localPosition/Scale/Rotation 설정 타입을 선택합니다. /// 자식 GameObject의 활성/비활성 상태를 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 이름을 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 레이어를 부모와 동일하게 설정할지 여부입니다. public static T[] AddBeforeSelfRange(this GameObject parent, IEnumerable childOriginals, TransformCloneType cloneType = TransformCloneType.KeepOriginal, bool? setActive = null, string specifiedName = null, bool setLayer = false) where T : UnityEngine.Object { var root = parent.Parent(); if (root == null) throw new InvalidOperationException("The parent root is null"); var sibilingIndex = parent.transform.GetSiblingIndex(); var child = AddRange(root, childOriginals, cloneType, setActive, specifiedName, setLayer); for (int i = child.Length - 1; i >= 0; i--) { var go = GetGameObject(child[i]); if (go == null) continue; go.transform.SetSiblingIndex(sibilingIndex); } return child; } /// /// GameObject/Component를 이 GameObject 뒤에 추가합니다. 대상이 복제됩니다. /// /// 부모 GameObject입니다. /// 복제할 대상입니다. /// 복제된 자식 GameObject의 localPosition/Scale/Rotation 설정 타입을 선택합니다. /// 자식 GameObject의 활성/비활성 상태를 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 이름을 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 레이어를 부모와 동일하게 설정할지 여부입니다. public static T AddAfterSelf(this GameObject parent, T childOriginal, TransformCloneType cloneType = TransformCloneType.KeepOriginal, bool? setActive = null, string specifiedName = null, bool setLayer = false) where T : UnityEngine.Object { var root = parent.Parent(); if (root == null) throw new InvalidOperationException("The parent root is null"); var sibilingIndex = parent.transform.GetSiblingIndex() + 1; var child = Add(root, childOriginal, cloneType, setActive, specifiedName, setLayer); var go = GetGameObject(child); if (go == null) return child; go.transform.SetSiblingIndex(sibilingIndex); return child; } /// /// GameObject/Component를 이 GameObject 뒤에 추가합니다. 대상이 복제됩니다. /// /// 부모 GameObject입니다. /// 복제할 대상입니다. /// 복제된 자식 GameObject의 localPosition/Scale/Rotation 설정 타입을 선택합니다. /// 자식 GameObject의 활성/비활성 상태를 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 이름을 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 레이어를 부모와 동일하게 설정할지 여부입니다. public static T[] AddAfterSelfRange(this GameObject parent, IEnumerable childOriginals, TransformCloneType cloneType = TransformCloneType.KeepOriginal, bool? setActive = null, string specifiedName = null, bool setLayer = false) where T : UnityEngine.Object { var root = parent.Parent(); if (root == null) throw new InvalidOperationException("The parent root is null"); var sibilingIndex = parent.transform.GetSiblingIndex() + 1; var child = AddRange(root, childOriginals, cloneType, setActive, specifiedName, setLayer); for (int i = child.Length - 1; i >= 0; i--) { var go = GetGameObject(child[i]); if (go == null) continue; go.transform.SetSiblingIndex(sibilingIndex); } return child; } #endregion #region Move /// /// GameObject/Component를 이 GameObject의 자식으로 이동합니다. /// /// 부모 GameObject입니다. /// 대상입니다. /// 이동된 자식 GameObject의 localPosition/Scale/Rotation 설정 타입을 선택합니다. /// 자식 GameObject의 활성/비활성 상태를 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 레이어를 부모와 동일하게 설정할지 여부입니다. public static T MoveToLast(this GameObject parent, T child, TransformMoveType moveType = TransformMoveType.DoNothing, bool? setActive = null, bool setLayer = false) where T : UnityEngine.Object { if (parent == null) throw new ArgumentNullException("parent"); if (child == null) throw new ArgumentNullException("child"); var childGameObject = GetGameObject(child); if (child == null) return child; // uGUI의 경우 SetParent(parent, false)를 사용해야 합니다 var childTransform = childGameObject.transform; #if !(UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5) var rectTransform = childTransform as RectTransform; if (rectTransform != null) { rectTransform.SetParent(parent.transform, worldPositionStays: false); } else { #endif var parentTransform = parent.transform; childTransform.parent = parentTransform; switch (moveType) { case TransformMoveType.FollowParent: childTransform.localPosition = parentTransform.localPosition; childTransform.localScale = parentTransform.localScale; childTransform.localRotation = parentTransform.localRotation; break; case TransformMoveType.Origin: childTransform.localPosition = Vector3.zero; childTransform.localScale = Vector3.one; childTransform.localRotation = Quaternion.identity; break; case TransformMoveType.DoNothing: default: break; } #if !(UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5) } #endif if (setLayer) { childGameObject.layer = parent.layer; } if (setActive != null) { childGameObject.SetActive(setActive.Value); } return child; } /// /// GameObject/Component를 이 GameObject의 자식으로 이동합니다. /// /// 부모 GameObject입니다. /// 대상입니다. /// 이동된 자식 GameObject의 localPosition/Scale/Rotation 설정 타입을 선택합니다. /// 자식 GameObject의 활성/비활성 상태를 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 레이어를 부모와 동일하게 설정할지 여부입니다. public static T[] MoveToLastRange(this GameObject parent, IEnumerable childs, TransformMoveType moveType = TransformMoveType.DoNothing, bool? setActive = null, bool setLayer = false) where T : UnityEngine.Object { if (parent == null) throw new ArgumentNullException("parent"); if (childs == null) throw new ArgumentNullException("childs"); // 반복 최적화 { var array = childs as T[]; if (array != null) { var result = new T[array.Length]; for (int i = 0; i < array.Length; i++) { var child = MoveToLast(parent, array[i], moveType, setActive, setLayer); result[i] = child; } return result; } } { var iterList = childs as IList; if (iterList != null) { var result = new T[iterList.Count]; for (int i = 0; i < iterList.Count; i++) { var child = MoveToLast(parent, iterList[i], moveType, setActive, setLayer); result[i] = child; } return result; } } { var result = new List(); foreach (var childOriginal in childs) { var child = MoveToLast(parent, childOriginal, moveType, setActive, setLayer); result.Add(child); } return result.ToArray(); } } /// /// GameObject/Component를 이 GameObject의 첫 번째 자식으로 이동합니다. /// /// 부모 GameObject입니다. /// 대상입니다. /// 이동된 자식 GameObject의 localPosition/Scale/Rotation 설정 타입을 선택합니다. /// 자식 GameObject의 활성/비활성 상태를 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 레이어를 부모와 동일하게 설정할지 여부입니다. public static T MoveToFirst(this GameObject parent, T child, TransformMoveType moveType = TransformMoveType.DoNothing, bool? setActive = null, bool setLayer = false) where T : UnityEngine.Object { MoveToLast(parent, child, moveType, setActive, setLayer); var go = GetGameObject(child); if (go == null) return child; go.transform.SetAsFirstSibling(); return child; } /// /// GameObject/Component를 이 GameObject의 첫 번째 자식으로 이동합니다. /// /// 부모 GameObject입니다. /// 대상입니다. /// 이동된 자식 GameObject의 localPosition/Scale/Rotation 설정 타입을 선택합니다. /// 자식 GameObject의 활성/비활성 상태를 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 레이어를 부모와 동일하게 설정할지 여부입니다. public static T[] MoveToFirstRange(this GameObject parent, IEnumerable childs, TransformMoveType moveType = TransformMoveType.DoNothing, bool? setActive = null, bool setLayer = false) where T : UnityEngine.Object { var child = MoveToLastRange(parent, childs, moveType, setActive, setLayer); for (int i = child.Length - 1; i >= 0; i--) { var go = GetGameObject(child[i]); if (go == null) continue; go.transform.SetAsFirstSibling(); } return child; } /// /// GameObject/Component를 이 GameObject 앞으로 이동합니다. /// /// 부모 GameObject입니다. /// 대상입니다. /// 이동된 자식 GameObject의 localPosition/Scale/Rotation 설정 타입을 선택합니다. /// 자식 GameObject의 활성/비활성 상태를 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 레이어를 부모와 동일하게 설정할지 여부입니다. public static T MoveToBeforeSelf(this GameObject parent, T child, TransformMoveType moveType = TransformMoveType.DoNothing, bool? setActive = null, bool setLayer = false) where T : UnityEngine.Object { var root = parent.Parent(); if (root == null) throw new InvalidOperationException("부모 루트가 null입니다"); var sibilingIndex = parent.transform.GetSiblingIndex(); MoveToLast(root, child, moveType, setActive, setLayer); var go = GetGameObject(child); if (go == null) return child; go.transform.SetSiblingIndex(sibilingIndex); return child; } /// /// GameObject/Component를 이 GameObject 앞으로 이동합니다. /// /// 부모 GameObject입니다. /// 대상입니다. /// 이동된 자식 GameObject의 localPosition/Scale/Rotation 설정 타입을 선택합니다. /// 자식 GameObject의 활성/비활성 상태를 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 레이어를 부모와 동일하게 설정할지 여부입니다. public static T[] MoveToBeforeSelfRange(this GameObject parent, IEnumerable childs, TransformMoveType moveType = TransformMoveType.DoNothing, bool? setActive = null, bool setLayer = false) where T : UnityEngine.Object { var root = parent.Parent(); if (root == null) throw new InvalidOperationException("부모 루트가 null입니다"); var sibilingIndex = parent.transform.GetSiblingIndex(); var child = MoveToLastRange(root, childs, moveType, setActive, setLayer); for (int i = child.Length - 1; i >= 0; i--) { var go = GetGameObject(child[i]); if (go == null) continue; go.transform.SetSiblingIndex(sibilingIndex); } return child; } /// /// GameObject/Component를 이 GameObject 뒤로 이동합니다. /// /// 부모 GameObject입니다. /// 대상입니다. /// 이동된 자식 GameObject의 localPosition/Scale/Rotation 설정 타입을 선택합니다. /// 자식 GameObject의 활성/비활성 상태를 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 레이어를 부모와 동일하게 설정할지 여부입니다. public static T MoveToAfterSelf(this GameObject parent, T child, TransformMoveType moveType = TransformMoveType.DoNothing, bool? setActive = null, bool setLayer = false) where T : UnityEngine.Object { var root = parent.Parent(); if (root == null) throw new InvalidOperationException("부모 루트가 null입니다"); var sibilingIndex = parent.transform.GetSiblingIndex() + 1; MoveToLast(root, child, moveType, setActive, setLayer); var go = GetGameObject(child); if (go == null) return child; go.transform.SetSiblingIndex(sibilingIndex); return child; } /// /// GameObject/Component를 이 GameObject 뒤로 이동합니다. /// /// 부모 GameObject입니다. /// 대상입니다. /// 이동된 자식 GameObject의 localPosition/Scale/Rotation 설정 타입을 선택합니다. /// 자식 GameObject의 활성/비활성 상태를 설정합니다. null이면 지정된 값을 설정하지 않습니다. /// 자식 GameObject의 레이어를 부모와 동일하게 설정할지 여부입니다. public static T[] MoveToAfterSelfRange(this GameObject parent, IEnumerable childs, TransformMoveType moveType = TransformMoveType.DoNothing, bool? setActive = null, bool setLayer = false) where T : UnityEngine.Object { var root = parent.Parent(); if (root == null) throw new InvalidOperationException("부모 루트가 null입니다"); var sibilingIndex = parent.transform.GetSiblingIndex() + 1; var child = MoveToLastRange(root, childs, moveType, setActive, setLayer); for (int i = child.Length - 1; i >= 0; i--) { var go = GetGameObject(child[i]); if (go == null) continue; go.transform.SetSiblingIndex(sibilingIndex); } return child; } #endregion /// 이 GameObject를 안전하게(null 체크) 파괴합니다. /// 에디터 모드에서는 true로 설정하거나 !Application.isPlaying을 전달하세요. /// 부모를 null로 설정합니다. public static void Destroy(this GameObject self, bool useDestroyImmediate = false, bool detachParent = false) { if (self == null) return; if (detachParent) { #if !(UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5) self.transform.SetParent(null); #else self.transform.parent = null; #endif } if (useDestroyImmediate) { GameObject.DestroyImmediate(self); } else { GameObject.Destroy(self); } } } }