using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; namespace BrewMonster.Scripts.Utils.Editor { public class MultiPrefabOverrideTool : EditorWindow { private List selectedPrefabInstances = new List(); private Vector2 scrollPosition; private bool showAdvancedOptions = false; private bool applyPropertyOverrides = true; private bool applyAddedComponents = true; private bool applyRemovedComponents = true; private bool applyAddedGameObjects = true; private bool applyRemovedGameObjects = true; private bool applyObjectReferences = true; [MenuItem("Tools/Brew Monster/Multi Prefab Override Tool")] public static void ShowWindow() { GetWindow("Multi Prefab Override"); } private void OnEnable() { RefreshSelection(); } private void OnSelectionChange() { RefreshSelection(); Repaint(); } private void RefreshSelection() { selectedPrefabInstances.Clear(); GameObject[] selected = Selection.gameObjects; foreach (GameObject obj in selected) { if (obj != null && PrefabUtility.IsPartOfPrefabInstance(obj)) { selectedPrefabInstances.Add(obj); } } } private void OnGUI() { EditorGUILayout.Space(10); EditorGUILayout.LabelField("Multi Prefab Override Tool", EditorStyles.boldLabel); EditorGUILayout.HelpBox("Select prefab instances in the scene to apply their overrides back to their prefabs.", MessageType.Info); EditorGUILayout.Space(10); // Selection info EditorGUILayout.LabelField($"Selected Prefab Instances: {selectedPrefabInstances.Count}", EditorStyles.boldLabel); if (selectedPrefabInstances.Count == 0) { EditorGUILayout.HelpBox("No prefab instances selected. Please select prefab instances in the scene.", MessageType.Warning); return; } // List of selected prefab instances scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, GUILayout.Height(200)); foreach (GameObject obj in selectedPrefabInstances) { if (obj == null) continue; EditorGUILayout.BeginHorizontal(); EditorGUILayout.ObjectField(obj, typeof(GameObject), true); PrefabAssetType prefabType = PrefabUtility.GetPrefabAssetType(obj); string prefabPath = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(obj); string displayName = string.IsNullOrEmpty(prefabPath) ? "Unknown" : System.IO.Path.GetFileName(prefabPath); EditorGUILayout.LabelField($"→ {displayName}", GUILayout.Width(200)); EditorGUILayout.EndHorizontal(); } EditorGUILayout.EndScrollView(); EditorGUILayout.Space(10); // Advanced options showAdvancedOptions = EditorGUILayout.Foldout(showAdvancedOptions, "Advanced Options", true); if (showAdvancedOptions) { EditorGUI.indentLevel++; applyPropertyOverrides = EditorGUILayout.Toggle("Apply Property Overrides", applyPropertyOverrides); applyAddedComponents = EditorGUILayout.Toggle("Apply Added Components", applyAddedComponents); applyRemovedComponents = EditorGUILayout.Toggle("Apply Removed Components", applyRemovedComponents); applyAddedGameObjects = EditorGUILayout.Toggle("Apply Added GameObjects", applyAddedGameObjects); applyRemovedGameObjects = EditorGUILayout.Toggle("Apply Removed GameObjects", applyRemovedGameObjects); applyObjectReferences = EditorGUILayout.Toggle("Apply Object References", applyObjectReferences); EditorGUI.indentLevel--; } EditorGUILayout.Space(10); // Action buttons EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Apply All Overrides", GUILayout.Height(30))) { ApplyAllOverrides(); } if (GUILayout.Button("Revert All Overrides", GUILayout.Height(30))) { RevertAllOverrides(); } EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(5); if (GUILayout.Button("Refresh Selection", GUILayout.Height(25))) { RefreshSelection(); } EditorGUILayout.Space(10); // Statistics if (selectedPrefabInstances.Count > 0) { EditorGUILayout.LabelField("Statistics:", EditorStyles.boldLabel); int totalOverrides = GetTotalOverrideCount(); EditorGUILayout.LabelField($"Total Overrides: {totalOverrides}"); } } private int GetTotalOverrideCount() { int count = 0; foreach (GameObject obj in selectedPrefabInstances) { if (obj == null) continue; count += PrefabUtility.GetObjectOverrides(obj).Count(); } return count; } private void ApplyAllOverrides() { if (selectedPrefabInstances.Count == 0) { EditorUtility.DisplayDialog("No Selection", "Please select prefab instances first.", "OK"); return; } if (!EditorUtility.DisplayDialog("Apply Overrides", $"This will apply all overrides from {selectedPrefabInstances.Count} prefab instance(s) back to their prefabs.\n\nThis action cannot be undone. Continue?", "Yes", "Cancel")) { return; } int successCount = 0; int failCount = 0; List failedObjects = new List(); Undo.SetCurrentGroupName("Apply Prefab Overrides"); int undoGroup = Undo.GetCurrentGroup(); foreach (GameObject obj in selectedPrefabInstances) { if (obj == null) continue; try { Undo.RegisterFullObjectHierarchyUndo(obj, "Apply Prefab Overrides"); // Apply overrides based on selected options if (applyPropertyOverrides || applyObjectReferences) { // Get all property overrides var propertyOverrides = PrefabUtility.GetObjectOverrides(obj); foreach (var objectOverride in propertyOverrides) { if (objectOverride.instanceObject == null) continue; PrefabUtility.ApplyObjectOverride(objectOverride.instanceObject, PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(obj), InteractionMode.AutomatedAction); } } if (applyAddedComponents) { // Apply added components var addedComponents = PrefabUtility.GetAddedComponents(obj); foreach (var addedComponent in addedComponents) { if (addedComponent.instanceComponent == null) continue; PrefabUtility.ApplyAddedComponent(addedComponent.instanceComponent, PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(obj), InteractionMode.AutomatedAction); } } if (applyRemovedComponents) { // Apply removed components (this removes them from the prefab) var removedComponents = PrefabUtility.GetRemovedComponents(obj); foreach (var removedComponent in removedComponents) { PrefabUtility.ApplyRemovedComponent(obj, removedComponent.assetComponent, InteractionMode.AutomatedAction); } } if (applyAddedGameObjects) { // Apply added GameObjects var addedGameObjects = PrefabUtility.GetAddedGameObjects(obj); foreach (var addedGameObject in addedGameObjects) { if (addedGameObject.instanceGameObject == null) continue; PrefabUtility.ApplyAddedGameObject(addedGameObject.instanceGameObject, PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(obj), InteractionMode.AutomatedAction); } } if (applyRemovedGameObjects) { // Apply removed GameObjects (this removes them from the prefab) var removedGameObjects = PrefabUtility.GetRemovedGameObjects(obj); foreach (var removedGameObject in removedGameObjects) { PrefabUtility.ApplyRemovedGameObject(obj, removedGameObject.assetGameObject, InteractionMode.AutomatedAction); } } successCount++; } catch (System.Exception e) { failCount++; failedObjects.Add($"{obj.name}: {e.Message}"); Debug.LogError($"Failed to apply overrides for {obj.name}: {e.Message}", obj); } } Undo.CollapseUndoOperations(undoGroup); // Mark scenes as dirty for (int i = 0; i < SceneManager.sceneCount; i++) { var scene = SceneManager.GetSceneAt(i); if (scene.isLoaded) { EditorSceneManager.MarkSceneDirty(scene); } } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); // Show results string message = $"Applied overrides to {successCount} prefab instance(s)."; if (failCount > 0) { message += $"\n\nFailed: {failCount} instance(s).\n\nFailed objects:\n" + string.Join("\n", failedObjects); } EditorUtility.DisplayDialog("Apply Overrides Complete", message, "OK"); Debug.Log($"Multi Prefab Override: {message}"); RefreshSelection(); Repaint(); } private void RevertAllOverrides() { if (selectedPrefabInstances.Count == 0) { EditorUtility.DisplayDialog("No Selection", "Please select prefab instances first.", "OK"); return; } if (!EditorUtility.DisplayDialog("Revert Overrides", $"This will revert all overrides from {selectedPrefabInstances.Count} prefab instance(s).\n\nThis action cannot be undone. Continue?", "Yes", "Cancel")) { return; } int successCount = 0; int failCount = 0; Undo.SetCurrentGroupName("Revert Prefab Overrides"); int undoGroup = Undo.GetCurrentGroup(); foreach (GameObject obj in selectedPrefabInstances) { if (obj == null) continue; try { Undo.RegisterFullObjectHierarchyUndo(obj, "Revert Prefab Overrides"); PrefabUtility.RevertPrefabInstance(obj, InteractionMode.AutomatedAction); successCount++; } catch (System.Exception e) { failCount++; Debug.LogError($"Failed to revert overrides for {obj.name}: {e.Message}", obj); } } Undo.CollapseUndoOperations(undoGroup); // Mark scenes as dirty for (int i = 0; i < SceneManager.sceneCount; i++) { var scene = SceneManager.GetSceneAt(i); if (scene.isLoaded) { EditorSceneManager.MarkSceneDirty(scene); } } string message = $"Reverted overrides from {successCount} prefab instance(s)."; if (failCount > 0) { message += $"\n\nFailed: {failCount} instance(s)."; } EditorUtility.DisplayDialog("Revert Overrides Complete", message, "OK"); Debug.Log($"Multi Prefab Override: {message}"); RefreshSelection(); Repaint(); } } }