347 lines
10 KiB
C#
347 lines
10 KiB
C#
using System.IO;
|
|
using UnityEditor;
|
|
using UnityEditor.AddressableAssets;
|
|
using UnityEditor.AddressableAssets.Settings;
|
|
using UnityEditor.SceneManagement;
|
|
using UnityEngine;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
namespace BrewMonster.Scripts.Utils.Editor
|
|
{
|
|
public static class ClearMissingScript
|
|
{
|
|
[MenuItem("Tools/Brew Monster/Clear Missing Script/Active Scene")]
|
|
public static void ClearMissingScriptsInActiveScene()
|
|
{
|
|
var scene = SceneManager.GetActiveScene();
|
|
if (!scene.IsValid())
|
|
{
|
|
EditorUtility.DisplayDialog("Clear Missing Script", "No valid active scene.", "OK");
|
|
return;
|
|
}
|
|
|
|
int removedCount = 0;
|
|
var roots = scene.GetRootGameObjects();
|
|
foreach (var root in roots)
|
|
{
|
|
Undo.RegisterFullObjectHierarchyUndo(root, "Clear Missing Scripts");
|
|
removedCount += RemoveMissingScriptsRecursive(root);
|
|
}
|
|
|
|
if (removedCount > 0)
|
|
{
|
|
EditorSceneManager.MarkSceneDirty(scene);
|
|
}
|
|
|
|
EditorUtility.DisplayDialog("Clear Missing Script", $"Removed {removedCount} missing script(s) from active scene.", "OK");
|
|
}
|
|
|
|
[MenuItem("Tools/Brew Monster/Clear Missing Script/Prefabs in Selected Folder", true)]
|
|
public static bool ValidateClearMissingScriptsInSelectedFolder()
|
|
{
|
|
Object selectedObject = Selection.activeObject;
|
|
if (selectedObject == null)
|
|
return false;
|
|
|
|
string folderPath = AssetDatabase.GetAssetPath(selectedObject);
|
|
return AssetDatabase.IsValidFolder(folderPath);
|
|
}
|
|
|
|
[MenuItem("Tools/Brew Monster/Clear Missing Script/Prefabs in Selected Folder")]
|
|
public static void ClearMissingScriptsInSelectedFolder()
|
|
{
|
|
Object selectedObject = Selection.activeObject;
|
|
if (selectedObject == null)
|
|
{
|
|
EditorUtility.DisplayDialog("Clear Missing Script",
|
|
"Please select a folder in the Project window first.", "OK");
|
|
return;
|
|
}
|
|
|
|
string folderPath = AssetDatabase.GetAssetPath(selectedObject);
|
|
if (!AssetDatabase.IsValidFolder(folderPath))
|
|
{
|
|
EditorUtility.DisplayDialog("Clear Missing Script",
|
|
"Selected object is not a valid folder. Please select a folder in the Project window.", "OK");
|
|
return;
|
|
}
|
|
|
|
if (!EditorUtility.DisplayDialog("Clear Missing Script in Folder",
|
|
$"This will process all prefabs in:\n{folderPath}\n\nContinue?",
|
|
"Yes", "Cancel"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ProcessPrefabsInFolder(folderPath);
|
|
}
|
|
|
|
[MenuItem("Tools/Brew Monster/Clear Missing Script/All Prefabs")]
|
|
public static void ClearMissingScriptsInPrefabs()
|
|
{
|
|
if (!EditorUtility.DisplayDialog("Clear Missing Script in Prefabs",
|
|
"This will process all prefabs in the project. This may take a while. Continue?",
|
|
"Yes", "Cancel"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ProcessPrefabsInFolder("Assets");
|
|
}
|
|
|
|
|
|
|
|
|
|
[MenuItem("Tools/Brew Monster/Set Addressable For Asset In Selected File (Group: models)")]
|
|
public static bool SetAddressableForAssetInSelectedFileGroupModels()
|
|
{
|
|
Object selectedObject = Selection.activeObject;
|
|
if (selectedObject == null)
|
|
return false;
|
|
|
|
AddressableAssetSettings settings = AddressableAssetSettingsDefaultObject.Settings;
|
|
if (settings == null)
|
|
{
|
|
EditorUtility.DisplayDialog("Set Addressable", "Addressable settings not found.", "OK");
|
|
return false;
|
|
}
|
|
|
|
AddressableAssetGroup modelsGroup = GetOrCreateAddressableGroup(settings, "models");
|
|
if (modelsGroup == null)
|
|
{
|
|
EditorUtility.DisplayDialog("Set Addressable", "Could not find or create Addressable group: models", "OK");
|
|
return false;
|
|
}
|
|
|
|
string inputFilePath = AssetDatabase.GetAssetPath(selectedObject);
|
|
if (!string.IsNullOrEmpty(inputFilePath))
|
|
{
|
|
inputFilePath = inputFilePath.Replace("Assets/", "");
|
|
inputFilePath = Path.Combine(Application.dataPath, inputFilePath);
|
|
|
|
if (!File.Exists(inputFilePath))
|
|
{
|
|
EditorUtility.DisplayDialog("Set Addressable", $"Input file does not exist:\n{inputFilePath}", "OK");
|
|
return false;
|
|
}
|
|
|
|
string[] lines = File.ReadAllLines(inputFilePath);
|
|
|
|
int addedOrMovedCount = 0;
|
|
int skippedCount = 0;
|
|
foreach (string rawLine in lines)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(rawLine))
|
|
continue;
|
|
|
|
string assetPath = rawLine.Trim().Trim('"').Replace("\\", "/");
|
|
if (assetPath.StartsWith(Application.dataPath.Replace("\\", "/")))
|
|
{
|
|
assetPath = "Assets" + assetPath.Substring(Application.dataPath.Replace("\\", "/").Length);
|
|
}
|
|
else if (!assetPath.StartsWith("Assets/"))
|
|
{
|
|
assetPath = assetPath.TrimStart('/');
|
|
assetPath = "Assets/" + assetPath;
|
|
}
|
|
|
|
string guid = AssetDatabase.AssetPathToGUID(assetPath);
|
|
if (string.IsNullOrEmpty(guid))
|
|
{
|
|
skippedCount++;
|
|
Debug.LogWarning($"[SetAddressableForAssetInSelectedFile] Invalid asset path in file: {assetPath}");
|
|
continue;
|
|
}
|
|
|
|
AddressableAssetEntry entry = settings.CreateOrMoveEntry(guid, modelsGroup);
|
|
if (entry == null)
|
|
{
|
|
skippedCount++;
|
|
Debug.LogWarning($"[SetAddressableForAssetInSelectedFile] Failed to create/move entry: {assetPath}");
|
|
continue;
|
|
}
|
|
|
|
entry.SetAddress(assetPath);
|
|
addedOrMovedCount++;
|
|
}
|
|
|
|
if (addedOrMovedCount > 0)
|
|
{
|
|
settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryModified, null, true);
|
|
AssetDatabase.SaveAssets();
|
|
}
|
|
|
|
EditorUtility.DisplayDialog(
|
|
"Set Addressable",
|
|
$"Processed {lines.Length} line(s).\nAdded/Moved: {addedOrMovedCount}\nSkipped: {skippedCount}\nGroup: models",
|
|
"OK");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static AddressableAssetGroup GetOrCreateAddressableGroup(AddressableAssetSettings settings, string groupName)
|
|
{
|
|
if (settings == null || string.IsNullOrEmpty(groupName))
|
|
return null;
|
|
|
|
AddressableAssetGroup group = settings.FindGroup(groupName);
|
|
if (group != null)
|
|
return group;
|
|
|
|
group = settings.CreateGroup(groupName, false, false, true, null);
|
|
if (group != null)
|
|
{
|
|
EditorUtility.SetDirty(settings);
|
|
AssetDatabase.SaveAssets();
|
|
}
|
|
|
|
return group;
|
|
}
|
|
|
|
private static void ProcessPrefabsInFolder(string folderPath)
|
|
{
|
|
string[] prefabGUIDs = AssetDatabase.FindAssets("t:Prefab", new[] { folderPath });
|
|
int totalPrefabs = prefabGUIDs.Length;
|
|
|
|
if (totalPrefabs == 0)
|
|
{
|
|
EditorUtility.DisplayDialog("Clear Missing Script",
|
|
$"No prefabs found in folder:\n{folderPath}", "OK");
|
|
return;
|
|
}
|
|
|
|
int processedCount = 0;
|
|
int totalRemovedCount = 0;
|
|
int prefabsWithRemovals = 0;
|
|
|
|
EditorUtility.DisplayProgressBar("Clear Missing Scripts", "Processing prefabs...", 0f);
|
|
|
|
try
|
|
{
|
|
foreach (string guid in prefabGUIDs)
|
|
{
|
|
string assetPath = AssetDatabase.GUIDToAssetPath(guid);
|
|
processedCount++;
|
|
|
|
EditorUtility.DisplayProgressBar("Clear Missing Scripts",
|
|
$"Processing: {assetPath} ({processedCount}/{totalPrefabs})",
|
|
(float)processedCount / totalPrefabs);
|
|
|
|
GameObject prefabRoot = PrefabUtility.LoadPrefabContents(assetPath);
|
|
if (prefabRoot != null)
|
|
{
|
|
int removedCount = RemoveMissingScriptsRecursive(prefabRoot);
|
|
|
|
if (removedCount > 0)
|
|
{
|
|
PrefabUtility.SaveAsPrefabAsset(prefabRoot, assetPath);
|
|
totalRemovedCount += removedCount;
|
|
prefabsWithRemovals++;
|
|
Debug.Log($"[ClearMissingScript] Removed {removedCount} missing script(s) from prefab: {assetPath}");
|
|
}
|
|
|
|
PrefabUtility.UnloadPrefabContents(prefabRoot);
|
|
}
|
|
}
|
|
|
|
AssetDatabase.SaveAssets();
|
|
}
|
|
finally
|
|
{
|
|
EditorUtility.ClearProgressBar();
|
|
}
|
|
|
|
EditorUtility.DisplayDialog("Clear Missing Script",
|
|
$"Processed {processedCount} prefab(s) in folder:\n{folderPath}\n\nRemoved {totalRemovedCount} missing script(s) from {prefabsWithRemovals} prefab(s).",
|
|
"OK");
|
|
}
|
|
|
|
[MenuItem("Tools/Brew Monster/Clear Missing Script/Scene and Prefabs")]
|
|
public static void ClearMissingScriptsInSceneAndPrefabs()
|
|
{
|
|
if (!EditorUtility.DisplayDialog("Clear Missing Script",
|
|
"This will process the active scene and all prefabs in the project. This may take a while. Continue?",
|
|
"Yes", "Cancel"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int sceneRemovedCount = 0;
|
|
var scene = SceneManager.GetActiveScene();
|
|
if (scene.IsValid())
|
|
{
|
|
var roots = scene.GetRootGameObjects();
|
|
foreach (var root in roots)
|
|
{
|
|
Undo.RegisterFullObjectHierarchyUndo(root, "Clear Missing Scripts");
|
|
sceneRemovedCount += RemoveMissingScriptsRecursive(root);
|
|
}
|
|
|
|
if (sceneRemovedCount > 0)
|
|
{
|
|
EditorSceneManager.MarkSceneDirty(scene);
|
|
}
|
|
}
|
|
|
|
string[] prefabGUIDs = AssetDatabase.FindAssets("t:Prefab");
|
|
int totalPrefabs = prefabGUIDs.Length;
|
|
int processedCount = 0;
|
|
int prefabRemovedCount = 0;
|
|
int prefabsWithRemovals = 0;
|
|
|
|
EditorUtility.DisplayProgressBar("Clear Missing Scripts", "Processing prefabs...", 0f);
|
|
|
|
try
|
|
{
|
|
foreach (string guid in prefabGUIDs)
|
|
{
|
|
string assetPath = AssetDatabase.GUIDToAssetPath(guid);
|
|
processedCount++;
|
|
|
|
EditorUtility.DisplayProgressBar("Clear Missing Scripts",
|
|
$"Processing: {assetPath} ({processedCount}/{totalPrefabs})",
|
|
(float)processedCount / totalPrefabs);
|
|
|
|
GameObject prefabRoot = PrefabUtility.LoadPrefabContents(assetPath);
|
|
if (prefabRoot != null)
|
|
{
|
|
int removedCount = RemoveMissingScriptsRecursive(prefabRoot);
|
|
|
|
if (removedCount > 0)
|
|
{
|
|
PrefabUtility.SaveAsPrefabAsset(prefabRoot, assetPath);
|
|
prefabRemovedCount += removedCount;
|
|
prefabsWithRemovals++;
|
|
}
|
|
|
|
PrefabUtility.UnloadPrefabContents(prefabRoot);
|
|
}
|
|
}
|
|
|
|
AssetDatabase.SaveAssets();
|
|
}
|
|
finally
|
|
{
|
|
EditorUtility.ClearProgressBar();
|
|
}
|
|
|
|
int totalRemoved = sceneRemovedCount + prefabRemovedCount;
|
|
EditorUtility.DisplayDialog("Clear Missing Script",
|
|
$"Scene: Removed {sceneRemovedCount} missing script(s).\n" +
|
|
$"Prefabs: Processed {processedCount} prefab(s), removed {prefabRemovedCount} missing script(s) from {prefabsWithRemovals} prefab(s).\n" +
|
|
$"Total: {totalRemoved} missing script(s) removed.",
|
|
"OK");
|
|
}
|
|
|
|
private static int RemoveMissingScriptsRecursive(GameObject gameObject)
|
|
{
|
|
int removed = GameObjectUtility.RemoveMonoBehavioursWithMissingScript(gameObject);
|
|
for (int i = 0; i < gameObject.transform.childCount; i++)
|
|
{
|
|
removed += RemoveMissingScriptsRecursive(gameObject.transform.GetChild(i).gameObject);
|
|
}
|
|
return removed;
|
|
}
|
|
}
|
|
} |