using System; using System.Collections.Generic; using UnityEditor; using UnityEngine; namespace MCPForUnity.Editor.Helpers { /// /// Provides common utility methods for working with Unity Prefab assets. /// public static class PrefabUtilityHelper { /// /// Gets the GUID for a prefab asset path. /// /// The Unity asset path (e.g., "Assets/Prefabs/MyPrefab.prefab") /// The GUID string, or null if the path is invalid. public static string GetPrefabGUID(string assetPath) { if (string.IsNullOrEmpty(assetPath)) { return null; } try { return AssetDatabase.AssetPathToGUID(assetPath); } catch (Exception ex) { McpLog.Warn($"Failed to get GUID for asset path '{assetPath}': {ex.Message}"); return null; } } /// /// Gets variant information if the prefab is a variant. /// /// The prefab GameObject to check. /// A tuple containing (isVariant, parentPath, parentGuid). public static (bool isVariant, string parentPath, string parentGuid) GetVariantInfo(GameObject prefabAsset) { if (prefabAsset == null) { return (false, null, null); } try { PrefabAssetType assetType = PrefabUtility.GetPrefabAssetType(prefabAsset); if (assetType != PrefabAssetType.Variant) { return (false, null, null); } GameObject parentAsset = PrefabUtility.GetCorrespondingObjectFromSource(prefabAsset); if (parentAsset == null) { return (true, null, null); } string parentPath = AssetDatabase.GetAssetPath(parentAsset); string parentGuid = GetPrefabGUID(parentPath); return (true, parentPath, parentGuid); } catch (Exception ex) { McpLog.Warn($"Failed to get variant info for '{prefabAsset.name}': {ex.Message}"); return (false, null, null); } } /// /// Gets the list of component type names on a GameObject. /// /// The GameObject to inspect. /// A list of component type full names. public static List GetComponentTypeNames(GameObject obj) { var typeNames = new List(); if (obj == null) { return typeNames; } try { var components = obj.GetComponents(); foreach (var component in components) { if (component != null) { typeNames.Add(component.GetType().FullName); } } } catch (Exception ex) { McpLog.Warn($"Failed to get component types for '{obj.name}': {ex.Message}"); } return typeNames; } /// /// Recursively counts all children in the hierarchy. /// /// The root transform to count from. /// Total number of children in the hierarchy. public static int CountChildrenRecursive(Transform transform) { if (transform == null) { return 0; } int count = transform.childCount; for (int i = 0; i < transform.childCount; i++) { count += CountChildrenRecursive(transform.GetChild(i)); } return count; } /// /// Gets the source prefab path for a nested prefab instance. /// /// The GameObject to check. /// The asset path of the source prefab, or null if not a nested prefab. public static string GetNestedPrefabPath(GameObject gameObject) { if (gameObject == null || !PrefabUtility.IsAnyPrefabInstanceRoot(gameObject)) { return null; } try { var sourcePrefab = PrefabUtility.GetCorrespondingObjectFromSource(gameObject); if (sourcePrefab != null) { return AssetDatabase.GetAssetPath(sourcePrefab); } } catch (Exception ex) { McpLog.Warn($"Failed to get nested prefab path for '{gameObject.name}': {ex.Message}"); } return null; } /// /// Gets the nesting depth of a prefab instance within the prefab hierarchy. /// Returns 0 for main prefab root, 1 for first-level nested, 2 for second-level, etc. /// Returns -1 for non-prefab-root objects. /// /// The GameObject to analyze. /// The root transform of the main prefab asset. /// Nesting depth (0=main root, 1+=nested), or -1 if not a prefab root. public static int GetPrefabNestingDepth(GameObject gameObject, Transform mainPrefabRoot) { if (gameObject == null) return -1; // Main prefab root if (gameObject.transform == mainPrefabRoot) return 0; // Not a prefab instance root if (!PrefabUtility.IsAnyPrefabInstanceRoot(gameObject)) return -1; // Calculate depth by walking up the hierarchy int depth = 0; Transform current = gameObject.transform; while (current != null && current != mainPrefabRoot) { if (PrefabUtility.IsAnyPrefabInstanceRoot(current.gameObject)) { depth++; } current = current.parent; } return depth; } /// /// Gets the parent prefab path for a nested prefab instance. /// Returns null for main prefab root or non-prefab objects. /// /// The GameObject to analyze. /// The root transform of the main prefab asset. /// The asset path of the parent prefab, or null if none. public static string GetParentPrefabPath(GameObject gameObject, Transform mainPrefabRoot) { if (gameObject == null || gameObject.transform == mainPrefabRoot) return null; if (!PrefabUtility.IsAnyPrefabInstanceRoot(gameObject)) return null; // Walk up the hierarchy to find the parent prefab instance Transform current = gameObject.transform.parent; while (current != null && current != mainPrefabRoot) { if (PrefabUtility.IsAnyPrefabInstanceRoot(current.gameObject)) { return GetNestedPrefabPath(current.gameObject); } current = current.parent; } // Parent is the main prefab root - get its asset path if (mainPrefabRoot != null) { return AssetDatabase.GetAssetPath(mainPrefabRoot.gameObject); } return null; } } }