using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; using System; using System.Xml.Linq; using System.Collections.Generic; using System.Linq; using Wwise.Wooduan.Components; using Wwise.Wooduan.Editor; using Wwise.Wooduan.Tools; namespace Wwise.Wooduan.Tools { /// /// Search for all components of a type and apply operations to them or their paired components. /// For example, you can search for all the Buttons and add a ButtonSound to each Button, or remove all the ButtonSounds from them. /// internal class ComponentPairsManipulator : AkWooduanAssetSearcher { // component pair for processing [Serializable] private class ComponentPair { internal Type SearchComponent; internal Type PairedComponent; internal bool WillSearch = true; internal ComponentPair() {} internal ComponentPair(Type search, Type paired) { SearchComponent = search; PairedComponent = paired; } // toggle displayed in window internal string ToggleName { get { if (SearchComponent == null) return string.Empty; var name = SearchComponent.Name; if (PairedComponent != null) name += " (" + PairedComponent.Name + ")"; return name; } } } //converts string to type [Serializable] private class CustomTypes { internal string SearchComponent = ""; internal string PairedComponent = ""; internal readonly ComponentPair Pair = new ComponentPair(); internal void Validate() { Pair.SearchComponent = AkWooduanScriptingTools.StringToType(SearchComponent); Pair.PairedComponent = AkWooduanScriptingTools.StringToType(PairedComponent); } internal bool BothTypesFilled() { return (Pair.PairedComponent != null && Pair.SearchComponent != null); } } #region Fields // do not search for inherited components private bool _explicitType = true; private bool _exportLog = true; private bool _removePairedAsWell = true; private readonly List _componentPairs = new List(); private readonly List _customPairs = new List(); private readonly List _customTypeList = new List(); private enum ActionType { RemoveSearchedComponents, RemovePairedComponents, RemoveUnpairedComponents, AddUnpairedComponents, FindComponents, FindPairedComponents, FindUnpairedComponents, } private ActionType _actionType; #endregion #region Init private void OnEnable() { _componentPairs.Add(new ComponentPair(typeof(Animator), typeof(AnimationSound))); _componentPairs.Add(new ComponentPair(typeof(Camera), typeof(AkAudioListener))); } private void SwapComponents() { foreach (var pair in _componentPairs) { var tempType = pair.SearchComponent; pair.SearchComponent = pair.PairedComponent; pair.PairedComponent = tempType; } } private void OnDisable() { _componentPairs.Clear(); } #endregion #region GUI private void OnGUI() { using (new EditorGUILayout.HorizontalScope(GUI.skin.box)) { GUILayout.BeginHorizontal(); GUILayout.Label("Quick Select Components"); if (GUILayout.Button("Swap", GUILayout.Width(60))) SwapComponents(); if (GUILayout.Button("Select All", GUILayout.Width(100))) SelectAllToggles(true); if (GUILayout.Button("Deselect All", GUILayout.Width(100))) SelectAllToggles(false); GUILayout.EndHorizontal(); } using (new EditorGUILayout.VerticalScope(GUI.skin.box)) { foreach (var componentData in _componentPairs) { componentData.WillSearch = GUILayout.Toggle(componentData.WillSearch, componentData.ToggleName); } } using (new EditorGUILayout.HorizontalScope(GUI.skin.box)) { IncludeA = GUILayout.Toggle(IncludeA, "Search in prefabs"); IncludeB = GUILayout.Toggle(IncludeB, "Search in scenes"); } using (new EditorGUILayout.HorizontalScope(GUI.skin.box)) { _explicitType = GUILayout.Toggle(_explicitType, "Explicit Type"); _exportLog = EditorGUILayout.BeginToggleGroup("Export log to xml", _exportLog); EditorGUILayout.EndToggleGroup(); } using (new EditorGUILayout.VerticalScope(GUI.skin.box)) { GUILayout.Label("Enter custom types, format like 'Button' and 'ButtonSound'"); CustomTypes toBeRemoved = null; foreach (var customPair in _customTypeList) { GUILayout.BeginHorizontal(); EditorGUIUtility.labelWidth = 80; customPair.SearchComponent = EditorGUILayout.TextField("Type", customPair.SearchComponent); customPair.PairedComponent = EditorGUILayout.TextField("Paired Type", customPair.PairedComponent); if (GUILayout.Button("-", EditorStyles.miniButtonRight, GUILayout.Width(20))) toBeRemoved = customPair; GUILayout.EndHorizontal(); } _customTypeList.Remove(toBeRemoved); if (GUILayout.Button("+")) _customTypeList.Add(new CustomTypes()); } using (new EditorGUILayout.VerticalScope(GUI.skin.box)) { _actionType = (ActionType) EditorGUILayout.EnumPopup("Action", _actionType); switch (_actionType) { case ActionType.FindComponents: GUILayout.Label("Search for a type of component and export locations of all instances"); _exportLog = true; break; case ActionType.FindPairedComponents: GUILayout.Label("Search for occurrences where a type of component and its paired\n" + "type of component are on the same GameObject"); _exportLog = true; break; case ActionType.FindUnpairedComponents: GUILayout.Label("Search for occurrences where a type of component doesn't have its\n" + " paired component on the same GameObject"); _exportLog = true; break; case ActionType.RemoveSearchedComponents: _removePairedAsWell = GUILayout.Toggle(_removePairedAsWell, "Remove paired component as well"); GUILayout.Label("Search for a type of component and remove all instances of the component"); break; case ActionType.RemovePairedComponents: GUILayout.Label("Search for occurrences where a type of component has its paired\n" + "component on the same GameObject and remove the paired component"); break; case ActionType.RemoveUnpairedComponents: GUILayout.Label("Search for occurrences where a type of component doesn't have its\n" + "paired component on the same GameObject and remove the component itself"); break; case ActionType.AddUnpairedComponents: GUILayout.Label("Search for occurrences where a type of component doesn't have its\n" + "paired component on the same GameObject and add the paired component"); break; } } AkWooduanGuiDrawer.DisplaySearchPath(ref SearchPath); if (GUILayout.Button("Start Searching! (Process can't undo)", EditorStyles.toolbarButton)) StartSearch(); } private void SelectAllToggles(bool status) { foreach (var pair in _componentPairs) { pair.WillSearch = status; } } #endregion #region Validate private bool ValidateComponents() { _customPairs.Clear(); switch (_actionType) { case ActionType.AddUnpairedComponents: case ActionType.FindUnpairedComponents: case ActionType.FindPairedComponents: case ActionType.RemovePairedComponents: case ActionType.RemoveUnpairedComponents: return TypeCheckBothComponents(); case ActionType.FindComponents: return TypeCheckSearchedComponent(); case ActionType.RemoveSearchedComponents: return _removePairedAsWell ? TypeCheckBothComponents() : TypeCheckSearchedComponent(); } return false; } private bool TypeCheckSearchedComponent() { foreach (var customTypes in _customTypeList) { customTypes.Validate(); if (customTypes.Pair.SearchComponent != null) _customPairs.Add(customTypes.Pair); else { EditorUtility.DisplayDialog("Error", "Invalid or empty type name!", "OK"); return false; } } return true; } private bool TypeCheckBothComponents() { foreach (var customTypes in _customTypeList) { customTypes.Validate(); if (customTypes.BothTypesFilled()) _customPairs.Add(customTypes.Pair); else { EditorUtility.DisplayDialog("Error", "Invalid or empty type name!", "OK"); return false; } } return true; } #endregion #region Search private void StartSearch() { if (!ValidateComponents()) return; XRoot.SetAttributeValue("Action", _actionType); // search in prefabs if (IncludeA) FindFiles(SearchPrefabs, "Searching Prefabs", "*.prefab"); // search in scenes if (IncludeB) { var currentScene = SceneManager.GetActiveScene().path; FindFiles(SearchScenes, "Searching Scenes", "*.unity"); EditorSceneManager.OpenScene(currentScene); } if (_exportLog) ExportToFile(); CleanUp(); } private void SearchPrefabs(string filePath) { var prefab = AssetDatabase.LoadAssetAtPath(filePath); var xPrefab = new XElement("Prefab"); xPrefab.SetAttributeValue("AssetPath", filePath); var edited = false; var allPairs = _componentPairs.Concat(_customPairs); foreach (var componentData in allPairs) { if (!componentData.WillSearch) continue; Component[] components = prefab.GetComponentsInChildren(componentData.SearchComponent, true); if (components.Length > 0) { if (ProcessComponents(components, componentData, xPrefab, true)) edited = true; } } if (edited) { if (_actionType != ActionType.FindComponents && _actionType != ActionType.FindPairedComponents && _actionType != ActionType.FindUnpairedComponents) AkWooduanComponentBackup.SaveComponentAsset(prefab, filePath); XRoot.Add(xPrefab); } } private void SearchScenes(string filePath) { EditorSceneManager.OpenScene(filePath); var scene = SceneManager.GetActiveScene(); var rootGameObjects = scene.GetRootGameObjects(); var xScene = new XElement("Scene"); xScene.SetAttributeValue("AssetPath", filePath); var edited = false; foreach (var gameObject in rootGameObjects) { var allPairs = _componentPairs.Concat(_customPairs); foreach (var pair in allPairs) { if (!pair.WillSearch) continue; Component[] components = gameObject.GetComponentsInChildren(pair.SearchComponent, true); if (components.Length > 0) { if (ProcessComponents(components, pair, xScene, false)) edited = true; } } } if (edited) { if (_actionType != ActionType.FindComponents && _actionType != ActionType.FindPairedComponents && _actionType != ActionType.FindUnpairedComponents) EditorSceneManager.SaveScene(scene, scene.path, false); XRoot.Add(xScene); } EditorSceneManager.CloseScene(scene, false); } private bool ProcessComponents(Component[] components, ComponentPair componentPair, XElement xElement, bool isSearchingPrefab) { var modified = false; foreach (var component in components) { if (_explicitType && component.GetType() != componentPair.SearchComponent) continue; #if UNITY_2018_3_OR_NEWER if (isSearchingPrefab && PrefabUtility.GetPrefabAssetType(component.gameObject) == PrefabAssetType.NotAPrefab) continue; #else if (isSearchingPrefab && PrefabUtility.GetPrefabType(component.gameObject) == PrefabType.None) continue; #endif TotalCount++; Component pairedComponent; switch (_actionType) { case ActionType.AddUnpairedComponents: pairedComponent = component.gameObject.GetComponent(componentPair.PairedComponent); if (!pairedComponent) { component.gameObject.AddComponent(componentPair.PairedComponent); EditedCount++; WriteXNode(xElement, component, componentPair.SearchComponent, componentPair.PairedComponent); modified = true; } break; case ActionType.RemoveSearchedComponents: if (componentPair.PairedComponent != null) { pairedComponent = component.gameObject.GetComponent(componentPair.PairedComponent); if (pairedComponent) DestroyImmediate(pairedComponent); } WriteXNode(xElement, component, componentPair.SearchComponent, componentPair.PairedComponent); DestroyImmediate(component, true); modified = true; break; case ActionType.RemovePairedComponents: pairedComponent = component.gameObject.GetComponent(componentPair.PairedComponent); if (pairedComponent) { EditedCount++; WriteXNode(xElement, component, componentPair.SearchComponent, componentPair.PairedComponent); DestroyImmediate(pairedComponent, true); modified = true; } break; case ActionType.RemoveUnpairedComponents: pairedComponent = component.gameObject.GetComponent(componentPair.PairedComponent); if (!pairedComponent) { EditedCount++; WriteXNode(xElement, component, componentPair.SearchComponent, componentPair.PairedComponent); DestroyImmediate(component, true); modified = true; } break; case ActionType.FindComponents: WriteXNode(xElement, component, componentPair.SearchComponent); break; case ActionType.FindUnpairedComponents: pairedComponent = component.gameObject.GetComponent(componentPair.PairedComponent); if (!pairedComponent) { EditedCount++; WriteXNode(xElement, component, componentPair.SearchComponent, componentPair.PairedComponent); } break; case ActionType.FindPairedComponents: pairedComponent = component.gameObject.GetComponent(componentPair.PairedComponent); if (pairedComponent) { EditedCount++; WriteXNode(xElement, component, componentPair.SearchComponent, componentPair.PairedComponent); } break; } } return modified; } #endregion #region XML private void WriteXNode(XElement element, Component component, Type searchType, Type pairedType = null) { if (!_exportLog) return; var xNode = new XElement("Component"); xNode.SetAttributeValue("Type", searchType.Name); if (pairedType != null) xNode.SetAttributeValue("PairedType", pairedType.Name); xNode.SetAttributeValue("GameObject", GetGameObjectPath(component.transform)); element.Add(xNode); } private void ExportToFile() { var fileName = EditorUtility.SaveFilePanel("Export Log", XmlDocDirectory, "Search Result", "xml"); if (string.IsNullOrEmpty(fileName)) return; AkWooduanScriptingTools.WriteXml(fileName, XRoot); switch (_actionType) { case ActionType.FindComponents: EditorUtility.DisplayDialog("Success!", "Found " + TotalCount + " components!", "OK"); break; case ActionType.RemoveSearchedComponents: EditorUtility.DisplayDialog("Success!", "Removed " + TotalCount + " components!", "OK"); break; case ActionType.AddUnpairedComponents: EditorUtility.DisplayDialog("Success!", "Found " + TotalCount + " components,\n" + "Added " + EditedCount + " paired components!", "OK"); break; case ActionType.RemovePairedComponents: EditorUtility.DisplayDialog("Success!", "Found " + TotalCount + " components,\n" + "Removed " + EditedCount + " paired components!", "OK"); break; case ActionType.RemoveUnpairedComponents: EditorUtility.DisplayDialog("Success!", "Found " + TotalCount + " components,\n" + "Removed " + EditedCount + " unpaired components!", "OK"); break; case ActionType.FindUnpairedComponents: EditorUtility.DisplayDialog("Success!", "Found " + TotalCount + " components,\n" + "and " + EditedCount + " unpaired components!", "OK"); break; case ActionType.FindPairedComponents: EditorUtility.DisplayDialog("Success!", "Found " + TotalCount + " components,\n" + "and " + EditedCount + " paired components!", "OK"); break; } } #endregion } }