338 lines
10 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|
|
|