Files
test/Assets/Editor/MultiPrefabOverrideTool.cs
T

338 lines
10 KiB
C#

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<GameObject> selectedPrefabInstances = new List<GameObject>();
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<MultiPrefabOverrideTool>("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<string> failedObjects = new List<string>();
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();
}
}
}