Files
AppleHillsProduction/Assets/Editor/Utilities/BatchComponentAdder.cs

228 lines
8.4 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace Editor.Utilities
{
public class BatchComponentAdder : EditorWindow
{
private Vector2 scrollPosition;
private List<GameObject> selectedPrefabs = new List<GameObject>();
private string searchText = "";
private List<Type> availableComponentTypes = new List<Type>();
private List<Type> filteredComponentTypes = new List<Type>();
private int selectedComponentIndex = -1;
private bool showScriptsOnly = true;
private bool showBuiltInComponents = false;
[MenuItem("Tools/Batch Component Adder")]
public static void ShowWindow()
{
GetWindow<BatchComponentAdder>("Batch Component Adder");
}
private void OnEnable()
{
// Get all component types when the window is opened
RefreshComponentTypes();
}
private void RefreshComponentTypes()
{
// Get all types that derive from Component
availableComponentTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly => assembly.GetTypes())
.Where(type => type.IsSubclassOf(typeof(Component)) && !type.IsAbstract)
.OrderBy(type => type.Name)
.ToList();
// Apply initial filtering
FilterComponentTypes();
}
private void FilterComponentTypes()
{
filteredComponentTypes = availableComponentTypes
.Where(type => {
if (!showBuiltInComponents && type.Namespace != null && type.Namespace.StartsWith("UnityEngine"))
return false;
if (showScriptsOnly && type.Namespace != null && type.Namespace.StartsWith("UnityEngine"))
return false;
if (!string.IsNullOrEmpty(searchText))
return type.Name.ToLower().Contains(searchText.ToLower());
return true;
})
.ToList();
// Reset selection if it's no longer valid
if (selectedComponentIndex >= filteredComponentTypes.Count)
selectedComponentIndex = -1;
}
private void OnGUI()
{
EditorGUILayout.BeginVertical();
EditorGUILayout.LabelField("Batch Component Adder", EditorStyles.boldLabel);
EditorGUILayout.HelpBox("Select multiple prefabs, choose a component type, and add it to the root of all selected prefabs.", MessageType.Info);
EditorGUILayout.Space();
// Prefab selection section
EditorGUILayout.LabelField("Selected Prefabs", EditorStyles.boldLabel);
if (GUILayout.Button("Add Selected Assets"))
{
AddSelectedAssets();
}
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, GUILayout.Height(150));
for (int i = 0; i < selectedPrefabs.Count; i++)
{
EditorGUILayout.BeginHorizontal();
selectedPrefabs[i] = (GameObject)EditorGUILayout.ObjectField(selectedPrefabs[i], typeof(GameObject), false);
if (GUILayout.Button("X", GUILayout.Width(20)))
{
selectedPrefabs.RemoveAt(i);
i--;
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndScrollView();
if (GUILayout.Button("Clear All"))
{
selectedPrefabs.Clear();
}
EditorGUILayout.Space();
// Component selection section
EditorGUILayout.LabelField("Component to Add", EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
showScriptsOnly = EditorGUILayout.Toggle("Scripts Only", showScriptsOnly);
showBuiltInComponents = EditorGUILayout.Toggle("Show Built-in Components", showBuiltInComponents);
EditorGUILayout.EndHorizontal();
string newSearchText = EditorGUILayout.TextField("Search", searchText);
if (newSearchText != searchText)
{
searchText = newSearchText;
FilterComponentTypes();
}
string[] componentNames = filteredComponentTypes.Select(t => t.Name).ToArray();
selectedComponentIndex = EditorGUILayout.Popup("Component Type", selectedComponentIndex, componentNames);
EditorGUILayout.Space();
// Validate and add the component
GUI.enabled = selectedPrefabs.Count > 0 && selectedComponentIndex >= 0 && selectedComponentIndex < filteredComponentTypes.Count;
if (GUILayout.Button("Add Component to Prefabs"))
{
AddComponentToPrefabs();
}
GUI.enabled = true;
EditorGUILayout.EndVertical();
}
private void AddSelectedAssets()
{
UnityEngine.Object[] selectedObjects = Selection.objects;
foreach (var obj in selectedObjects)
{
if (obj is GameObject go)
{
string path = AssetDatabase.GetAssetPath(go);
if (!string.IsNullOrEmpty(path) && path.EndsWith(".prefab"))
{
if (!selectedPrefabs.Contains(go))
{
selectedPrefabs.Add(go);
}
}
}
}
}
private void AddComponentToPrefabs()
{
if (selectedComponentIndex < 0 || selectedComponentIndex >= filteredComponentTypes.Count)
return;
Type componentType = filteredComponentTypes[selectedComponentIndex];
int successCount = 0;
List<string> failedPrefabs = new List<string>();
// For undo operations
Undo.RecordObjects(selectedPrefabs.ToArray(), "Add Component To Prefabs");
foreach (GameObject prefab in selectedPrefabs)
{
// Skip null entries
if (prefab == null) continue;
try
{
// Open the prefab for editing
string prefabPath = AssetDatabase.GetAssetPath(prefab);
GameObject prefabRoot = PrefabUtility.LoadPrefabContents(prefabPath);
// Check if the component already exists
if (prefabRoot.GetComponent(componentType) == null)
{
// Add the component
prefabRoot.AddComponent(componentType);
// Save the prefab
PrefabUtility.SaveAsPrefabAsset(prefabRoot, prefabPath);
successCount++;
}
else
{
failedPrefabs.Add($"{prefab.name} (already has component)");
}
// Unload the prefab
PrefabUtility.UnloadPrefabContents(prefabRoot);
}
catch (Exception e)
{
Debug.LogError($"Error adding component to {prefab.name}: {e.Message}");
failedPrefabs.Add($"{prefab.name} (error)");
}
}
// Show results
if (successCount > 0)
{
Debug.Log($"Successfully added {componentType.Name} to {successCount} prefabs.");
}
if (failedPrefabs.Count > 0)
{
Debug.LogWarning($"Failed to add component to {failedPrefabs.Count} prefabs: {string.Join(", ", failedPrefabs)}");
}
// Refresh the asset database to show changes
AssetDatabase.Refresh();
}
}
}