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 selectedPrefabs = new List(); private string searchText = ""; private List availableComponentTypes = new List(); private List filteredComponentTypes = new List(); private int selectedComponentIndex = -1; private bool showScriptsOnly = true; private bool showBuiltInComponents = false; [MenuItem("Tools/Batch Component Adder")] public static void ShowWindow() { GetWindow("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 failedPrefabs = new List(); // 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(); } } }