You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

239 lines
7.4 KiB

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEditor;
  5. using System;
  6. using System.Linq;
  7. /// <summary>
  8. /// Editor Class used to search and replace objects with prefabs;
  9. /// </summary>
  10. public class MassReplacer : EditorWindow {
  11. #region Editor Settings
  12. //base settings
  13. private string FilterText;
  14. private ListItem[] FilteredList;
  15. private GameObject CopiedPrefab;
  16. //advanced settings
  17. private bool doCopyScale = true;
  18. private bool doCopyRot = true;
  19. private bool isCaseSensitive = false;
  20. #endregion Editor Settings
  21. #region Private Variables
  22. private Vector2 scrollPosition;
  23. private string prevFilterText;
  24. private bool showAdvancedSettings;
  25. #endregion Private Variables
  26. #region GUI Functions
  27. [MenuItem("Tools/Mass Replace %e")]
  28. static void createWindow()
  29. {
  30. MassReplacer window = (MassReplacer)GetWindow(typeof(MassReplacer), true, "Replace", true);
  31. window.ReFilterList(window.FilterText);
  32. window.Repaint();
  33. }
  34. void OnGUI()
  35. {
  36. Event e = Event.current;
  37. //Base Settings
  38. EditorGUILayout.LabelField("Base Settings", EditorStyles.boldLabel);
  39. FilterText = EditorGUILayout.TextField((FilterText == null || FilterText == "") ? "Search..." : FilterText); //If textFiled is empty display "Searching..." else display search filter
  40. CopiedPrefab = (GameObject)EditorGUILayout.ObjectField(CopiedPrefab, typeof(GameObject), true);
  41. //Advanced Settings
  42. GUIStyle BoldFold = new GUIStyle(EditorStyles.foldout); BoldFold.font = EditorStyles.boldFont; //create foldoutstyle with bold text
  43. showAdvancedSettings = EditorGUILayout.Foldout(showAdvancedSettings, "Advanced Settings", BoldFold);
  44. if (showAdvancedSettings) {
  45. doCopyScale = EditorGUILayout.Toggle("Copy Scale", doCopyScale);
  46. doCopyRot = EditorGUILayout.Toggle("Copy Rotation", doCopyRot);
  47. isCaseSensitive = EditorGUILayout.Toggle("Case Sensitive Search", isCaseSensitive);
  48. }
  49. float advancedSettingHeight = 3 * ((showAdvancedSettings) ? 18 : 0);
  50. //Start of List
  51. GUILayout.Box("", GUILayout.Height(1), GUILayout.ExpandWidth(true));
  52. Rect scrollRect = GUILayoutUtility.GetLastRect();
  53. scrollPosition = GUILayout.BeginScrollView(scrollPosition, GUILayout.Width(position.width), GUILayout.Height(position.height - (108 + advancedSettingHeight) ));
  54. scrollRect.height = position.height - (108 + advancedSettingHeight);
  55. //Each Item
  56. foreach (ListItem listItem in FilteredList) {
  57. GUILayout.BeginHorizontal();
  58. listItem.isRepplaceable = GUILayout.Toggle(listItem.isRepplaceable, "", GUILayout.MaxWidth(10));
  59. GUILayout.Label(listItem.gameObject.name);
  60. Rect tempRect = GUILayoutUtility.GetLastRect();
  61. tempRect.position -= Vector2.down * (82 + advancedSettingHeight) + scrollPosition;
  62. if (tempRect.Overlaps(scrollRect))
  63. listItem.rect = tempRect;
  64. GUILayout.EndHorizontal();
  65. }
  66. GUILayout.EndScrollView(); //end list
  67. //Buttons at bottom of window
  68. GUILayout.BeginHorizontal();
  69. GUI.enabled = (CopiedPrefab != null);
  70. if (GUILayout.Button("Replace")) {
  71. replaceFiltedList();
  72. }
  73. GUI.enabled = true;
  74. if (GUILayout.Button("Filter To Selected")) {
  75. foreach(ListItem item in FilteredList)
  76. {
  77. item.isRepplaceable = Selection.gameObjects.Contains(item.gameObject);
  78. }
  79. }
  80. GUILayout.EndHorizontal(); // end buttons
  81. //Interaction
  82. //Check if filter is updated;
  83. if (prevFilterText != FilterText) {
  84. ReFilterList(FilterText);
  85. }
  86. //If clicked window check where clicked happened
  87. if (e.button == 0 && e.type == EventType.MouseDown)
  88. OnWindowClick(e.mousePosition);
  89. }
  90. /// <summary>
  91. /// Logic of when the window has been clicked
  92. /// </summary>
  93. /// <param name="mousePos">position of mouse when clicked</param>
  94. /// <returns></returns>
  95. private void OnWindowClick(Vector2 mousePos)
  96. {
  97. //Loops through each asset to see if one has been clicked on
  98. foreach (ListItem listItem in FilteredList) {
  99. if (listItem.rect.Contains(mousePos)) {
  100. Debug.Log("clicked: " + listItem.gameObject.name);
  101. Selection.activeGameObject = listItem.gameObject;
  102. break;
  103. }//end if
  104. }//end foreach
  105. }
  106. #endregion GUI Functions
  107. #region Search Functions
  108. #region Helper Functions
  109. private void ReFilterList(string filter)
  110. {
  111. if (filter == null)
  112. filter = "";
  113. StringComparison caseComparer = (isCaseSensitive) ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
  114. FilteredList = getFilteredList(filter, caseComparer);
  115. prevFilterText = filter;
  116. Repaint();
  117. }
  118. #endregion Helper Fucntions
  119. #region Static Functions
  120. /// <summary>
  121. /// Returns an array of listItems of gameobjects in scene which contains a string
  122. /// </summary>
  123. /// <param name="filter">look for gameobjects containing this string</param>
  124. /// <returns></returns>
  125. private static ListItem[] getFilteredList(string filter, StringComparison caseComparer = StringComparison.OrdinalIgnoreCase)
  126. {
  127. List<ListItem> retVal = new List<ListItem>();
  128. object[] obj = FindObjectsOfType(typeof(GameObject));
  129. foreach (object o in obj) {
  130. GameObject g = (GameObject)o;
  131. if (g.name.Contains(filter, caseComparer)) {
  132. ListItem newItem = new ListItem(g);
  133. retVal.Add(newItem);
  134. }
  135. }
  136. return retVal.ToArray();
  137. }
  138. #endregion Static Functions
  139. #endregion Search Functions
  140. #region Replace Fucntions
  141. private void replaceFiltedList()
  142. {
  143. foreach (ListItem listItem in FilteredList) {
  144. if (!listItem.isRepplaceable || listItem.gameObject.Equals(CopiedPrefab))
  145. continue;
  146. GameObject replaceObject = (GameObject)PrefabUtility.InstantiatePrefab(CopiedPrefab);
  147. replaceObject.transform.parent = listItem.gameObject.transform.parent;
  148. replaceObject.transform.position = listItem.gameObject.transform.position;
  149. replaceObject.name = listItem.gameObject.name;
  150. if (doCopyRot)
  151. replaceObject.transform.rotation = listItem.gameObject.transform.rotation;
  152. if (doCopyScale)
  153. replaceObject.transform.localScale = listItem.gameObject.transform.localScale;
  154. Undo.RegisterCreatedObjectUndo(replaceObject, "Replaced Objects (" + FilterText + ")");
  155. Undo.DestroyObjectImmediate(listItem.gameObject);
  156. }
  157. ReFilterList(FilterText);
  158. }
  159. #endregion Replace Fucntions
  160. class ListItem {
  161. public GameObject gameObject;
  162. public bool isRepplaceable;
  163. public Rect rect;
  164. public ListItem(GameObject gameObject)
  165. {
  166. this.gameObject = gameObject;
  167. isRepplaceable = true;
  168. rect = new Rect();
  169. }
  170. }
  171. }
  172. static class MassReplaceOverides {
  173. public static bool Contains(this string source, string toCheck, StringComparison comp)
  174. {
  175. return source != null && toCheck != null && source.IndexOf(toCheck, comp) >= 0;
  176. }
  177. }