diff --git a/MCPForUnity/Editor/Helpers/GameObjectLookup.cs b/MCPForUnity/Editor/Helpers/GameObjectLookup.cs index 0a4fbc27a..60d2b0967 100644 --- a/MCPForUnity/Editor/Helpers/GameObjectLookup.cs +++ b/MCPForUnity/Editor/Helpers/GameObjectLookup.cs @@ -3,6 +3,7 @@ using System.Linq; using Newtonsoft.Json.Linq; using UnityEditor; +using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; @@ -147,19 +148,45 @@ private static IEnumerable SearchByName(string name, bool includeInactive, private static IEnumerable SearchByPath(string path, bool includeInactive) { + // Check Prefab Stage first - GameObject.Find() doesn't work in Prefab Stage + var prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); + if (prefabStage != null) + { + // Use GetAllSceneObjects which already handles Prefab Stage + var allObjects = GetAllSceneObjects(includeInactive); + foreach (var go in allObjects) + { + if (MatchesPath(go, path)) + { + yield return go.GetInstanceID(); + } + } + yield break; + } + + // Normal scene mode // NOTE: Unity's GameObject.Find(path) only finds ACTIVE GameObjects. - // The includeInactive parameter has no effect here due to Unity API limitations. - // Consider using by_name search with includeInactive if you need to find inactive objects. + // If includeInactive=true, we need to search manually to find inactive objects. if (includeInactive) { - McpLog.Warn("[GameObjectLookup] SearchByPath with includeInactive=true: " + - "GameObject.Find() cannot find inactive objects. Use by_name search instead."); + // Search manually to support inactive objects + var allObjects = GetAllSceneObjects(true); + foreach (var go in allObjects) + { + if (MatchesPath(go, path)) + { + yield return go.GetInstanceID(); + } + } } - - var found = GameObject.Find(path); - if (found != null) + else { - yield return found.GetInstanceID(); + // Use GameObject.Find for active objects only (Unity API limitation) + var found = GameObject.Find(path); + if (found != null) + { + yield return found.GetInstanceID(); + } } } @@ -249,6 +276,19 @@ private static IEnumerable SearchByComponent(string componentTypeName, bool /// public static IEnumerable GetAllSceneObjects(bool includeInactive) { + // Check Prefab Stage first + var prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); + if (prefabStage != null && prefabStage.prefabContentsRoot != null) + { + // Use Prefab Stage's prefabContentsRoot + foreach (var go in GetObjectAndDescendants(prefabStage.prefabContentsRoot, includeInactive)) + { + yield return go; + } + yield break; + } + + // Normal scene mode var scene = SceneManager.GetActiveScene(); if (!scene.IsValid()) yield break; @@ -290,6 +330,18 @@ public static Type FindComponentType(string typeName) return UnityTypeResolver.ResolveComponent(typeName); } + /// + /// Checks whether a GameObject matches a path or trailing path segment. + /// + internal static bool MatchesPath(GameObject go, string path) + { + if (go == null || string.IsNullOrEmpty(path)) + return false; + + var goPath = GetGameObjectPath(go); + return goPath == path || goPath.EndsWith("/" + path); + } + /// /// Gets the hierarchical path of a GameObject. /// diff --git a/MCPForUnity/Editor/Tools/GameObjects/ManageGameObjectCommon.cs b/MCPForUnity/Editor/Tools/GameObjects/ManageGameObjectCommon.cs index 38d743f12..e686145ef 100644 --- a/MCPForUnity/Editor/Tools/GameObjects/ManageGameObjectCommon.cs +++ b/MCPForUnity/Editor/Tools/GameObjects/ManageGameObjectCommon.cs @@ -5,6 +5,7 @@ using MCPForUnity.Editor.Helpers; using MCPForUnity.Editor.Tools; using Newtonsoft.Json.Linq; +using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; @@ -83,11 +84,34 @@ internal static List FindObjectsInternal( break; case "by_path": - Transform foundTransform = rootSearchObject - ? rootSearchObject.transform.Find(searchTerm) - : GameObject.Find(searchTerm)?.transform; - if (foundTransform != null) - results.Add(foundTransform.gameObject); + if (rootSearchObject != null) + { + Transform foundTransform = rootSearchObject.transform.Find(searchTerm); + if (foundTransform != null) + results.Add(foundTransform.gameObject); + } + else + { + var prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); + if (prefabStage != null || searchInactive) + { + // In Prefab Stage, GameObject.Find() doesn't work, need to search manually + var allObjects = GetAllSceneObjects(searchInactive); + foreach (var go in allObjects) + { + if (GameObjectLookup.MatchesPath(go, searchTerm)) + { + results.Add(go); + } + } + } + else + { + var found = GameObject.Find(searchTerm); + if (found != null) + results.Add(found); + } + } break; case "by_tag": @@ -154,7 +178,12 @@ internal static List FindObjectsInternal( } } - GameObject objByPath = GameObject.Find(searchTerm); + // Try path search - in Prefab Stage, GameObject.Find() doesn't work + var allObjectsForPath = GetAllSceneObjects(true); + GameObject objByPath = allObjectsForPath.FirstOrDefault(go => + { + return GameObjectLookup.MatchesPath(go, searchTerm); + }); if (objByPath != null) { results.Add(objByPath); @@ -180,16 +209,8 @@ internal static List FindObjectsInternal( private static IEnumerable GetAllSceneObjects(bool includeInactive) { - var rootObjects = SceneManager.GetActiveScene().GetRootGameObjects(); - var allObjects = new List(); - foreach (var root in rootObjects) - { - allObjects.AddRange( - root.GetComponentsInChildren(includeInactive) - .Select(t => t.gameObject) - ); - } - return allObjects; + // Delegate to GameObjectLookup to avoid code duplication and ensure consistent behavior + return GameObjectLookup.GetAllSceneObjects(includeInactive); } private static Type FindType(string typeName) diff --git a/MCPForUnity/Editor/Tools/ManageScene.cs b/MCPForUnity/Editor/Tools/ManageScene.cs index 8bc5456c2..e58c09424 100644 --- a/MCPForUnity/Editor/Tools/ManageScene.cs +++ b/MCPForUnity/Editor/Tools/ManageScene.cs @@ -490,8 +490,21 @@ private static object GetSceneHierarchyPaged(SceneCommand cmd) { try { - try { McpLog.Info("[ManageScene] get_hierarchy: querying EditorSceneManager.GetActiveScene", always: false); } catch { } - Scene activeScene = EditorSceneManager.GetActiveScene(); + // Check Prefab Stage first + var prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); + Scene activeScene; + + if (prefabStage != null) + { + activeScene = prefabStage.scene; + try { McpLog.Info("[ManageScene] get_hierarchy: using Prefab Stage scene", always: false); } catch { } + } + else + { + try { McpLog.Info("[ManageScene] get_hierarchy: querying EditorSceneManager.GetActiveScene", always: false); } catch { } + activeScene = EditorSceneManager.GetActiveScene(); + } + try { McpLog.Info($"[ManageScene] get_hierarchy: got scene valid={activeScene.IsValid()} loaded={activeScene.isLoaded} name='{activeScene.name}'", always: false); } catch { } if (!activeScene.IsValid() || !activeScene.isLoaded) { @@ -599,7 +612,16 @@ private static GameObject ResolveGameObject(JToken targetToken, Scene activeScen // Path-based find (e.g., "Root/Child/GrandChild") if (s.Contains("/")) { - try { return GameObject.Find(s); } catch { } + try + { + var ids = GameObjectLookup.SearchGameObjects("by_path", s, includeInactive: true, maxResults: 1); + if (ids.Count > 0) + { + var byPath = GameObjectLookup.FindById(ids[0]); + if (byPath != null) return byPath; + } + } + catch { } } // Name-based find (first match, includes inactive)