ObjectGrouper.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. 
  2. /*WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW*\ ( ( ) )
  3. |/ \| ) ) _((_
  4. || (c) Wanzyee Studio < wanzyeestudio.blogspot.com > || ( ( |_ _ |=n
  5. |\ /| _____)) | ! ] U
  6. \.ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ./ (_(__(S) |___*/
  7. using UnityEditor;
  8. using UnityEngine;
  9. using System;
  10. using System.Linq;
  11. using Object = UnityEngine.Object;
  12. namespace WanzyeeStudio.Editrix.Toolkit{
  13. /// <summary>
  14. /// Group or ungroup scene <c>UnityEngine.Transform</c> for better arrangement.
  15. /// </summary>
  16. ///
  17. /// <remarks>
  18. /// Menu "GameObject/Group/Make Group" or hotkey Ctrl-G to group selected children.
  19. /// Menu "GameObject/Group/Ungroup Parent" or hotkey Ctrl-Shift-G to ungroup selected parents.
  20. /// Menu "GameObject/Group/Ungroup Children" or hotkey Ctrl-Alt-G to move selected children out from the group.
  21. /// </remarks>
  22. ///
  23. public static class ObjectGrouper{
  24. #region Menu
  25. /// <summary>
  26. /// Make a group <c>UnityEngine.Transform</c> contains selected children in the scene, with hotkey Ctrl-G.
  27. /// </summary>
  28. [MenuItem("GameObject/Group/Make Group %g", false, 90)]
  29. public static void MakeGroup(){
  30. if(!MakeGroupValid()) throw new InvalidOperationException("No transform selected.");
  31. Selection.objects = new Object[]{MakeGroup(null, Selection.transforms).gameObject};
  32. }
  33. /// <summary>
  34. /// Check if <c>MakeGroup()</c> valid, any scene <c>UnityEngine.Transform</c> selected.
  35. /// </summary>
  36. /// <returns><c>true</c>, if valid.</returns>
  37. [MenuItem("GameObject/Group/Make Group %g", true)]
  38. private static bool MakeGroupValid(){
  39. return Selection.transforms.Any();
  40. }
  41. /// <summary>
  42. /// Ungroup children <c>UnityEngine.Transform</c> from selected parent in the scene, with hotkey Ctrl-Shift-G.
  43. /// </summary>
  44. ///
  45. /// <remarks>
  46. /// This'll destroy the parent if it has no other <c>UnityEngine.Component</c>.
  47. /// Works like <c>Transform.DetachChildren</c> but detach to upward parent instead of root.
  48. /// </remarks>
  49. ///
  50. /*
  51. * It causes crash when called with both destroying and selecting at once sometimes.
  52. * It can be avoided by delay call, it may be an Unity internal execution order problem.
  53. */
  54. [MenuItem("GameObject/Group/Ungroup Parent %#g", false, 90)]
  55. public static void UngroupParent(){
  56. if(!UngroupParentValid()) throw new InvalidOperationException("No parent transform selected.");
  57. EditorApplication.delayCall += () => {
  58. var _p = Selection.transforms.Where(_v => 0 < _v.childCount);
  59. var _c = _p.SelectMany(_v => UngroupParent(_v, 2 > _v.GetComponents<Component>().Length));
  60. Selection.objects = _c.Select(_v => _v.gameObject).ToArray();
  61. };
  62. }
  63. /// <summary>
  64. /// Check if <c>UngroupParent()</c> valid, any scene parent <c>UnityEngine.Transform</c> selected.
  65. /// </summary>
  66. /// <returns><c>true</c>, if valid.</returns>
  67. [MenuItem("GameObject/Group/Ungroup Parent %#g", true)]
  68. private static bool UngroupParentValid(){
  69. return Selection.transforms.Any(_v => 0 < _v.childCount);
  70. }
  71. /// <summary>
  72. /// Ungroup selected children <c>UnityEngine.Transform</c> to upward parent, with hotkey Ctrl-Alt-G.
  73. /// </summary>
  74. [MenuItem("GameObject/Group/Ungroup Children %&g", false, 90)]
  75. public static void UngroupChildren(){
  76. if(!UngroupChildrenValid()) throw new InvalidOperationException("No child transform selected.");
  77. var _c = Selection.transforms.Where(_v => null != _v.parent).OrderBy(_v => -_v.GetSiblingIndex());
  78. foreach(var _v in _c) UngroupChild(_v);
  79. }
  80. /// <summary>
  81. /// Check if <c>UngroupParent()</c> valid, any scene child <c>UnityEngine.Transform</c> selected.
  82. /// </summary>
  83. /// <returns><c>true</c>, if valid.</returns>
  84. [MenuItem("GameObject/Group/Ungroup Children %&g", true)]
  85. private static bool UngroupChildrenValid(){
  86. return Selection.transforms.Any(_v => null != _v.parent);
  87. }
  88. #endregion
  89. #region Methods
  90. /// <summary>
  91. /// Filter the top level transforms, excluding prefabs, ordered by sibling index.
  92. /// </summary>
  93. /// <returns>The tops.</returns>
  94. /// <param name="transforms">Transforms.</param>
  95. public static Transform[] FilterTops(params Transform[] transforms){
  96. if(null == transforms) throw new ArgumentNullException("transforms");
  97. var _t = transforms.Distinct().Where(_v => null != _v && !EditorUtility.IsPersistent(_v)).ToArray();
  98. var _f = _t.Where(_v => !_t.Any(_o => _o != _v && _v.IsChildOf(_o)));
  99. return _f.OrderBy(_v => EditrixUtility.GetObjectOrder(_v)).ToArray();
  100. }
  101. /// <summary>
  102. /// Make a group <c>UnityEngine.Transform</c> contains specified children.
  103. /// </summary>
  104. /// <returns>The group parent.</returns>
  105. /// <param name="name">Name.</param>
  106. /// <param name="children">Children.</param>
  107. public static Transform MakeGroup(string name, params Transform[] children){
  108. if(null == children) throw new ArgumentNullException("children");
  109. children = FilterTops(children);
  110. if(!children.Any()) throw new ArgumentException("No children assigned.", "children");
  111. var _g = new GameObject(string.IsNullOrEmpty(name) ? "New Group" : name);
  112. var _result = children.All(_v => _v is RectTransform) ? _g.AddComponent<RectTransform>() : _g.transform;
  113. Undo.RegisterCreatedObjectUndo(_g, "Make Group");
  114. Undo.SetTransformParent(_result, children[0].parent, "Arrange Group");
  115. _result.SetSiblingIndex(children[0].GetSiblingIndex());
  116. foreach(var _v in children) Undo.SetTransformParent(_v, _result, "Join Group");
  117. return _result;
  118. }
  119. /// <summary>
  120. /// Ungroup all children <c>UnityEngine.Transform</c> from specified parent.
  121. /// </summary>
  122. ///
  123. /// <remarks>
  124. /// Optional to destroy the original parent after done.
  125. /// Works like <c>UnityEngine.Transform.DetachChildren</c> but detach to upward parent instead of root.
  126. /// </remarks>
  127. ///
  128. /// <returns>The children from the parent.</returns>
  129. /// <param name="parent">Parent.</param>
  130. /// <param name="destroy">If set to <c>true</c> destroy.</param>
  131. ///
  132. public static Transform[] UngroupParent(Transform parent, bool destroy = false){
  133. if(null == parent) throw new ArgumentNullException("parent");
  134. var _result = parent.Cast<Transform>().Reverse().ToArray();
  135. if(!_result.Any()) throw new ArgumentException("No children included.", "parent");
  136. foreach(var _v in _result) UngroupChild(_v);
  137. if(destroy) Undo.DestroyObjectImmediate(parent.gameObject);
  138. return _result;
  139. }
  140. /// <summary>
  141. /// Ungroup a child <c>UnityEngine.Transform</c> from current parent to upward.
  142. /// </summary>
  143. /// <returns>The new parent.</returns>
  144. /// <param name="child">Child.</param>
  145. public static Transform UngroupChild(Transform child){
  146. if(null == child) throw new ArgumentNullException("child");
  147. var _p = child.parent;
  148. if(null == _p) throw new ArgumentException("Given child is a root.", "child");
  149. Undo.SetTransformParent(child, _p.parent, "Ungroup Child");
  150. child.SetAsLastSibling();
  151. child.SetSiblingIndex(_p.GetSiblingIndex() + 1);
  152. return child.parent;
  153. }
  154. #endregion
  155. }
  156. }