650 lines
28 KiB
C#
650 lines
28 KiB
C#
using UnityEngine;
|
|
using UnityEditor;
|
|
using UnityEditor.SceneManagement;
|
|
using UnityEngine.SceneManagement;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using Interactions;
|
|
|
|
namespace Editor
|
|
{
|
|
public class RemoveInteractableBaseComponents : EditorWindow
|
|
{
|
|
private List<string> problematicPrefabs = new List<string>();
|
|
private List<string> problematicScenes = new List<string>();
|
|
private Vector2 scrollPosition;
|
|
private bool hasScanned;
|
|
private int componentsFound;
|
|
|
|
[MenuItem("AppleHills/Developer/Remove InteractableBase Components")]
|
|
public static void ShowWindow()
|
|
{
|
|
var window = GetWindow<RemoveInteractableBaseComponents>("Remove InteractableBase");
|
|
window.minSize = new Vector2(700, 500);
|
|
}
|
|
|
|
private void OnGUI()
|
|
{
|
|
GUILayout.Label("Remove InteractableBase Component References", EditorStyles.boldLabel);
|
|
|
|
EditorGUILayout.HelpBox(
|
|
"This tool finds and removes EXACT InteractableBase components from prefabs and scenes.\n\n" +
|
|
"Only finds the bare base class, NOT derived types like Pickup/ItemSlot/OneClickInteraction.\n\n" +
|
|
"If components depend on InteractableBase, you'll be prompted to replace it.",
|
|
MessageType.Info);
|
|
|
|
EditorGUILayout.Space();
|
|
|
|
if (GUILayout.Button("Scan All Prefabs and Scenes", GUILayout.Height(35)))
|
|
{
|
|
ScanAll();
|
|
}
|
|
|
|
EditorGUILayout.Space();
|
|
|
|
if (hasScanned)
|
|
{
|
|
EditorGUILayout.LabelField($"Found {componentsFound} exact InteractableBase components", EditorStyles.boldLabel);
|
|
EditorGUILayout.LabelField($"In {problematicPrefabs.Count} prefabs");
|
|
EditorGUILayout.LabelField($"In {problematicScenes.Count} scenes");
|
|
|
|
if (componentsFound > 0)
|
|
{
|
|
EditorGUILayout.Space();
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
|
|
if (problematicPrefabs.Count > 0 && GUILayout.Button($"Remove from Prefabs ({problematicPrefabs.Count})", GUILayout.Height(35)))
|
|
{
|
|
RemoveFromAllPrefabs();
|
|
}
|
|
|
|
if (problematicScenes.Count > 0 && GUILayout.Button($"Remove from Scenes ({problematicScenes.Count})", GUILayout.Height(35)))
|
|
{
|
|
RemoveFromAllScenes();
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUILayout.Space();
|
|
|
|
if (GUILayout.Button("Remove All (Prefabs + Scenes)", GUILayout.Height(35)))
|
|
{
|
|
RemoveAll();
|
|
}
|
|
|
|
EditorGUILayout.Space();
|
|
|
|
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
|
|
|
if (problematicPrefabs.Count > 0)
|
|
{
|
|
EditorGUILayout.LabelField("Prefabs:", EditorStyles.boldLabel);
|
|
foreach (var prefabPath in problematicPrefabs)
|
|
{
|
|
EditorGUILayout.BeginHorizontal("box");
|
|
EditorGUILayout.LabelField(prefabPath);
|
|
if (GUILayout.Button("Remove", GUILayout.Width(80)))
|
|
{
|
|
RemoveFromPrefab(prefabPath);
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
EditorGUILayout.Space();
|
|
}
|
|
|
|
if (problematicScenes.Count > 0)
|
|
{
|
|
EditorGUILayout.LabelField("Scenes:", EditorStyles.boldLabel);
|
|
foreach (var scenePath in problematicScenes)
|
|
{
|
|
EditorGUILayout.BeginHorizontal("box");
|
|
EditorGUILayout.LabelField(scenePath);
|
|
if (GUILayout.Button("Remove", GUILayout.Width(80)))
|
|
{
|
|
RemoveFromScene(scenePath);
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.EndScrollView();
|
|
}
|
|
else
|
|
{
|
|
EditorGUILayout.HelpBox("No exact InteractableBase components found! All clean.", MessageType.Info);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ScanAll()
|
|
{
|
|
problematicPrefabs.Clear();
|
|
problematicScenes.Clear();
|
|
componentsFound = 0;
|
|
hasScanned = true;
|
|
|
|
ScanPrefabs();
|
|
ScanScenes();
|
|
|
|
Debug.Log($"<color=cyan>[Scan Complete]</color> Found {componentsFound} exact InteractableBase components in {problematicPrefabs.Count} prefabs and {problematicScenes.Count} scenes.");
|
|
}
|
|
|
|
private void ScanPrefabs()
|
|
{
|
|
string[] prefabGuids = AssetDatabase.FindAssets("t:Prefab", new[] { "Assets" });
|
|
|
|
EditorUtility.DisplayProgressBar("Scanning Prefabs", "Starting...", 0f);
|
|
|
|
for (int i = 0; i < prefabGuids.Length; i++)
|
|
{
|
|
string path = AssetDatabase.GUIDToAssetPath(prefabGuids[i]);
|
|
|
|
EditorUtility.DisplayProgressBar("Scanning Prefabs",
|
|
$"Checking {i + 1}/{prefabGuids.Length}: {Path.GetFileName(path)}",
|
|
(float)i / prefabGuids.Length);
|
|
|
|
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
|
if (prefab != null)
|
|
{
|
|
// Check if this prefab or any of its children have EXACTLY InteractableBase (not derived types)
|
|
InteractableBase[] components = prefab.GetComponentsInChildren<InteractableBase>(true);
|
|
int exactMatches = 0;
|
|
foreach (var component in components)
|
|
{
|
|
if (component != null && component.GetType() == typeof(InteractableBase))
|
|
{
|
|
exactMatches++;
|
|
}
|
|
}
|
|
|
|
if (exactMatches > 0)
|
|
{
|
|
problematicPrefabs.Add(path);
|
|
componentsFound += exactMatches;
|
|
}
|
|
}
|
|
}
|
|
|
|
EditorUtility.ClearProgressBar();
|
|
}
|
|
|
|
private void ScanScenes()
|
|
{
|
|
string[] sceneGuids = AssetDatabase.FindAssets("t:Scene", new[] { "Assets/Scenes" });
|
|
|
|
EditorUtility.DisplayProgressBar("Scanning Scenes", "Starting...", 0f);
|
|
|
|
string currentScenePath = SceneManager.GetActiveScene().path;
|
|
|
|
for (int i = 0; i < sceneGuids.Length; i++)
|
|
{
|
|
string path = AssetDatabase.GUIDToAssetPath(sceneGuids[i]);
|
|
|
|
EditorUtility.DisplayProgressBar("Scanning Scenes",
|
|
$"Checking {i + 1}/{sceneGuids.Length}: {Path.GetFileName(path)}",
|
|
(float)i / sceneGuids.Length);
|
|
|
|
EditorSceneManager.OpenScene(path, OpenSceneMode.Single);
|
|
|
|
// Find all InteractableBase components in the scene
|
|
InteractableBase[] components = GameObject.FindObjectsByType<InteractableBase>(FindObjectsSortMode.None);
|
|
|
|
int exactMatches = 0;
|
|
foreach (var component in components)
|
|
{
|
|
if (component != null && component.GetType() == typeof(InteractableBase))
|
|
{
|
|
exactMatches++;
|
|
}
|
|
}
|
|
|
|
if (exactMatches > 0)
|
|
{
|
|
problematicScenes.Add(path);
|
|
componentsFound += exactMatches;
|
|
}
|
|
}
|
|
|
|
// Restore original scene
|
|
if (!string.IsNullOrEmpty(currentScenePath))
|
|
{
|
|
EditorSceneManager.OpenScene(currentScenePath);
|
|
}
|
|
|
|
EditorUtility.ClearProgressBar();
|
|
}
|
|
|
|
private void RemoveFromAllPrefabs()
|
|
{
|
|
if (!EditorUtility.DisplayDialog("Confirm Removal",
|
|
$"This will remove InteractableBase components from {problematicPrefabs.Count} prefabs.\n\n" +
|
|
"This cannot be undone (unless you use version control).\n\nContinue?",
|
|
"Yes, Remove", "Cancel"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int removedCount = 0;
|
|
|
|
for (int i = 0; i < problematicPrefabs.Count; i++)
|
|
{
|
|
string path = problematicPrefabs[i];
|
|
|
|
EditorUtility.DisplayProgressBar("Removing Components from Prefabs",
|
|
$"Processing {i + 1}/{problematicPrefabs.Count}: {Path.GetFileName(path)}",
|
|
(float)i / problematicPrefabs.Count);
|
|
|
|
removedCount += RemoveFromPrefab(path);
|
|
}
|
|
|
|
EditorUtility.ClearProgressBar();
|
|
AssetDatabase.SaveAssets();
|
|
AssetDatabase.Refresh();
|
|
|
|
Debug.Log($"<color=green>[Prefab Cleanup Complete]</color> Removed {removedCount} InteractableBase components from prefabs.");
|
|
|
|
ScanAll();
|
|
}
|
|
|
|
private void RemoveFromAllScenes()
|
|
{
|
|
if (!EditorUtility.DisplayDialog("Confirm Removal",
|
|
$"This will remove InteractableBase components from {problematicScenes.Count} scenes.\n\n" +
|
|
"This cannot be undone (unless you use version control).\n\nContinue?",
|
|
"Yes, Remove", "Cancel"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int removedCount = 0;
|
|
string currentScenePath = SceneManager.GetActiveScene().path;
|
|
|
|
for (int i = 0; i < problematicScenes.Count; i++)
|
|
{
|
|
string path = problematicScenes[i];
|
|
|
|
EditorUtility.DisplayProgressBar("Removing Components from Scenes",
|
|
$"Processing {i + 1}/{problematicScenes.Count}: {Path.GetFileName(path)}",
|
|
(float)i / problematicScenes.Count);
|
|
|
|
removedCount += RemoveFromScene(path);
|
|
}
|
|
|
|
// Restore original scene
|
|
if (!string.IsNullOrEmpty(currentScenePath))
|
|
{
|
|
EditorSceneManager.OpenScene(currentScenePath);
|
|
}
|
|
|
|
EditorUtility.ClearProgressBar();
|
|
AssetDatabase.SaveAssets();
|
|
AssetDatabase.Refresh();
|
|
|
|
Debug.Log($"<color=green>[Scene Cleanup Complete]</color> Removed {removedCount} InteractableBase components from scenes.");
|
|
|
|
ScanAll();
|
|
}
|
|
|
|
private void RemoveAll()
|
|
{
|
|
if (!EditorUtility.DisplayDialog("Confirm Removal",
|
|
$"This will remove {componentsFound} InteractableBase components from:\n" +
|
|
$"• {problematicPrefabs.Count} prefabs\n" +
|
|
$"• {problematicScenes.Count} scenes\n\n" +
|
|
"This cannot be undone (unless you use version control).\n\nContinue?",
|
|
"Yes, Remove", "Cancel"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int removedCount = 0;
|
|
|
|
// Remove from prefabs
|
|
for (int i = 0; i < problematicPrefabs.Count; i++)
|
|
{
|
|
string path = problematicPrefabs[i];
|
|
|
|
EditorUtility.DisplayProgressBar("Removing Components",
|
|
$"Prefabs {i + 1}/{problematicPrefabs.Count}: {Path.GetFileName(path)}",
|
|
(float)i / (problematicPrefabs.Count + problematicScenes.Count));
|
|
|
|
removedCount += RemoveFromPrefab(path);
|
|
}
|
|
|
|
// Remove from scenes
|
|
string currentScenePath = SceneManager.GetActiveScene().path;
|
|
|
|
for (int i = 0; i < problematicScenes.Count; i++)
|
|
{
|
|
string path = problematicScenes[i];
|
|
|
|
EditorUtility.DisplayProgressBar("Removing Components",
|
|
$"Scenes {i + 1}/{problematicScenes.Count}: {Path.GetFileName(path)}",
|
|
(float)(problematicPrefabs.Count + i) / (problematicPrefabs.Count + problematicScenes.Count));
|
|
|
|
removedCount += RemoveFromScene(path);
|
|
}
|
|
|
|
// Restore original scene
|
|
if (!string.IsNullOrEmpty(currentScenePath))
|
|
{
|
|
EditorSceneManager.OpenScene(currentScenePath);
|
|
}
|
|
|
|
EditorUtility.ClearProgressBar();
|
|
AssetDatabase.SaveAssets();
|
|
AssetDatabase.Refresh();
|
|
|
|
Debug.Log($"<color=green>[Removal Complete]</color> Removed {removedCount} InteractableBase components.");
|
|
|
|
ScanAll();
|
|
}
|
|
|
|
private int RemoveFromPrefab(string assetPath)
|
|
{
|
|
int removed = 0;
|
|
|
|
try
|
|
{
|
|
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
|
|
if (prefab == null)
|
|
{
|
|
Debug.LogWarning($"Could not load prefab at path: {assetPath}");
|
|
return 0;
|
|
}
|
|
|
|
string prefabPath = AssetDatabase.GetAssetPath(prefab);
|
|
GameObject prefabContents = null;
|
|
|
|
try
|
|
{
|
|
prefabContents = PrefabUtility.LoadPrefabContents(prefabPath);
|
|
}
|
|
catch (Exception loadEx)
|
|
{
|
|
Debug.LogError($"Failed to load prefab contents for {assetPath}: {loadEx.Message}");
|
|
return 0;
|
|
}
|
|
|
|
if (prefabContents == null)
|
|
{
|
|
Debug.LogWarning($"Prefab contents are null for: {assetPath}");
|
|
return 0;
|
|
}
|
|
|
|
InteractableBase[] components = prefabContents.GetComponentsInChildren<InteractableBase>(true);
|
|
|
|
if (components == null || components.Length == 0)
|
|
{
|
|
PrefabUtility.UnloadPrefabContents(prefabContents);
|
|
return 0;
|
|
}
|
|
|
|
foreach (var component in components)
|
|
{
|
|
if (component == null)
|
|
continue;
|
|
|
|
// Check if it's EXACTLY InteractableBase (not a derived type)
|
|
if (component.GetType() == typeof(InteractableBase))
|
|
{
|
|
// Cache references before destroying
|
|
GameObject targetObject = component.gameObject;
|
|
string objectName = targetObject != null ? targetObject.name : "Unknown";
|
|
|
|
// Check if GameObject already has a derived InteractableBase type
|
|
bool hasPickup = targetObject.GetComponent<Pickup>() != null;
|
|
bool hasItemSlot = targetObject.GetComponent<ItemSlot>() != null;
|
|
bool hasOneClick = targetObject.GetComponent<OneClickInteraction>() != null;
|
|
|
|
if (hasPickup || hasItemSlot || hasOneClick)
|
|
{
|
|
// GameObject already has a concrete type, safe to remove bare base class
|
|
DestroyImmediate(component);
|
|
removed++;
|
|
string existingType = hasItemSlot ? "ItemSlot" : (hasPickup ? "Pickup" : "OneClickInteraction");
|
|
Debug.Log($"<color=green>[Removed]</color> Bare InteractableBase from '{objectName}' (already has {existingType}) in prefab '{Path.GetFileName(assetPath)}'");
|
|
continue;
|
|
}
|
|
|
|
// Check what other components depend on InteractableBase
|
|
Component[] allComponents = targetObject.GetComponents<Component>();
|
|
List<string> dependentComponents = new List<string>();
|
|
|
|
foreach (var otherComponent in allComponents)
|
|
{
|
|
if (otherComponent == null || otherComponent == component)
|
|
continue;
|
|
|
|
var requireAttributes = otherComponent.GetType().GetCustomAttributes(typeof(RequireComponent), true);
|
|
foreach (RequireComponent attr in requireAttributes)
|
|
{
|
|
if (attr.m_Type0 == typeof(InteractableBase) ||
|
|
attr.m_Type1 == typeof(InteractableBase) ||
|
|
attr.m_Type2 == typeof(InteractableBase))
|
|
{
|
|
dependentComponents.Add(otherComponent.GetType().Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dependentComponents.Count > 0)
|
|
{
|
|
string dependencyList = string.Join(", ", dependentComponents);
|
|
string message = $"GameObject '{objectName}' in prefab '{Path.GetFileName(assetPath)}' has InteractableBase, " +
|
|
$"but these components depend on it:\n\n{dependencyList}\n\n" +
|
|
"Replace InteractableBase with:";
|
|
|
|
int choice = EditorUtility.DisplayDialogComplex(
|
|
"Component Dependency Detected",
|
|
message,
|
|
"Pickup",
|
|
"ItemSlot",
|
|
"OneClickInteraction");
|
|
|
|
Type replacementType = choice switch
|
|
{
|
|
0 => typeof(Pickup),
|
|
1 => typeof(ItemSlot),
|
|
2 => typeof(OneClickInteraction),
|
|
_ => null
|
|
};
|
|
|
|
if (replacementType != null)
|
|
{
|
|
// Cache component data before destroying
|
|
bool isOneTime = component.isOneTime;
|
|
float cooldown = component.cooldown;
|
|
CharacterToInteract characterToInteract = component.characterToInteract;
|
|
|
|
DestroyImmediate(component);
|
|
|
|
var newComponent = targetObject.AddComponent(replacementType) as InteractableBase;
|
|
if (newComponent != null)
|
|
{
|
|
newComponent.isOneTime = isOneTime;
|
|
newComponent.cooldown = cooldown;
|
|
newComponent.characterToInteract = characterToInteract;
|
|
|
|
removed++;
|
|
Debug.Log($"<color=cyan>[Replaced]</color> InteractableBase with {replacementType.Name} on '{objectName}' in prefab '{Path.GetFileName(assetPath)}'");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning($"Skipped removing InteractableBase from '{objectName}' - no replacement chosen");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DestroyImmediate(component);
|
|
removed++;
|
|
Debug.Log($"<color=yellow>[Removed]</color> InteractableBase from '{objectName}' in prefab '{Path.GetFileName(assetPath)}'");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (removed > 0)
|
|
{
|
|
try
|
|
{
|
|
PrefabUtility.SaveAsPrefabAsset(prefabContents, prefabPath);
|
|
}
|
|
catch (Exception saveEx)
|
|
{
|
|
Debug.LogError($"Failed to save prefab {assetPath}: {saveEx.Message}");
|
|
}
|
|
}
|
|
|
|
PrefabUtility.UnloadPrefabContents(prefabContents);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogError($"Error removing components from prefab {assetPath}: {ex.Message}\nStack: {ex.StackTrace}");
|
|
}
|
|
|
|
return removed;
|
|
}
|
|
|
|
private int RemoveFromScene(string scenePath)
|
|
{
|
|
int removed = 0;
|
|
|
|
try
|
|
{
|
|
Scene scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
|
|
|
|
if (!scene.isLoaded)
|
|
{
|
|
Debug.LogWarning($"Scene not loaded: {scenePath}");
|
|
return 0;
|
|
}
|
|
|
|
InteractableBase[] components = GameObject.FindObjectsByType<InteractableBase>(FindObjectsSortMode.None);
|
|
|
|
if (components == null || components.Length == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
foreach (var component in components)
|
|
{
|
|
if (component == null)
|
|
continue;
|
|
|
|
if (component.GetType() == typeof(InteractableBase))
|
|
{
|
|
// Cache references before destroying
|
|
GameObject targetObject = component.gameObject;
|
|
string objectName = targetObject != null ? targetObject.name : "Unknown";
|
|
|
|
// Check if GameObject already has a derived InteractableBase type
|
|
bool hasPickup = targetObject.GetComponent<Pickup>() != null;
|
|
bool hasItemSlot = targetObject.GetComponent<ItemSlot>() != null;
|
|
bool hasOneClick = targetObject.GetComponent<OneClickInteraction>() != null;
|
|
|
|
if (hasPickup || hasItemSlot || hasOneClick)
|
|
{
|
|
// GameObject already has a concrete type, safe to remove bare base class
|
|
DestroyImmediate(component);
|
|
removed++;
|
|
string existingType = hasItemSlot ? "ItemSlot" : (hasPickup ? "Pickup" : "OneClickInteraction");
|
|
Debug.Log($"<color=green>[Removed]</color> Bare InteractableBase from '{objectName}' (already has {existingType}) in scene '{Path.GetFileName(scenePath)}'");
|
|
continue;
|
|
}
|
|
|
|
Component[] allComponents = targetObject.GetComponents<Component>();
|
|
List<string> dependentComponents = new List<string>();
|
|
|
|
foreach (var otherComponent in allComponents)
|
|
{
|
|
if (otherComponent == null || otherComponent == component)
|
|
continue;
|
|
|
|
var requireAttributes = otherComponent.GetType().GetCustomAttributes(typeof(RequireComponent), true);
|
|
foreach (RequireComponent attr in requireAttributes)
|
|
{
|
|
if (attr.m_Type0 == typeof(InteractableBase) ||
|
|
attr.m_Type1 == typeof(InteractableBase) ||
|
|
attr.m_Type2 == typeof(InteractableBase))
|
|
{
|
|
dependentComponents.Add(otherComponent.GetType().Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dependentComponents.Count > 0)
|
|
{
|
|
string dependencyList = string.Join(", ", dependentComponents);
|
|
string message = $"GameObject '{objectName}' in scene '{Path.GetFileName(scenePath)}' has InteractableBase, " +
|
|
$"but these components depend on it:\n\n{dependencyList}\n\n" +
|
|
"Replace InteractableBase with:";
|
|
|
|
int choice = EditorUtility.DisplayDialogComplex(
|
|
"Component Dependency Detected",
|
|
message,
|
|
"Pickup",
|
|
"ItemSlot",
|
|
"OneClickInteraction");
|
|
|
|
Type replacementType = choice switch
|
|
{
|
|
0 => typeof(Pickup),
|
|
1 => typeof(ItemSlot),
|
|
2 => typeof(OneClickInteraction),
|
|
_ => null
|
|
};
|
|
|
|
if (replacementType != null)
|
|
{
|
|
// Cache component data before destroying
|
|
bool isOneTime = component.isOneTime;
|
|
float cooldown = component.cooldown;
|
|
CharacterToInteract characterToInteract = component.characterToInteract;
|
|
|
|
DestroyImmediate(component);
|
|
|
|
var newComponent = targetObject.AddComponent(replacementType) as InteractableBase;
|
|
if (newComponent != null)
|
|
{
|
|
newComponent.isOneTime = isOneTime;
|
|
newComponent.cooldown = cooldown;
|
|
newComponent.characterToInteract = characterToInteract;
|
|
|
|
removed++;
|
|
Debug.Log($"<color=cyan>[Replaced]</color> InteractableBase with {replacementType.Name} on '{objectName}' in scene '{Path.GetFileName(scenePath)}'");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning($"Skipped removing InteractableBase from '{objectName}' - no replacement chosen");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DestroyImmediate(component);
|
|
removed++;
|
|
Debug.Log($"<color=yellow>[Removed]</color> InteractableBase from '{objectName}' in scene '{Path.GetFileName(scenePath)}'");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (removed > 0)
|
|
{
|
|
EditorSceneManager.MarkSceneDirty(scene);
|
|
EditorSceneManager.SaveScene(scene);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogError($"Error removing components from scene {scenePath}: {ex.Message}\nStack: {ex.StackTrace}");
|
|
}
|
|
|
|
return removed;
|
|
}
|
|
}
|
|
}
|
|
|