Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 60 additions & 8 deletions MCPForUnity/Editor/Helpers/GameObjectLookup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;

Expand Down Expand Up @@ -147,19 +148,45 @@ private static IEnumerable<int> SearchByName(string name, bool includeInactive,

private static IEnumerable<int> 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();
}
}
}

Expand Down Expand Up @@ -249,6 +276,19 @@ private static IEnumerable<int> SearchByComponent(string componentTypeName, bool
/// </summary>
public static IEnumerable<GameObject> 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;
Expand Down Expand Up @@ -290,6 +330,18 @@ public static Type FindComponentType(string typeName)
return UnityTypeResolver.ResolveComponent(typeName);
}

/// <summary>
/// Checks whether a GameObject matches a path or trailing path segment.
/// </summary>
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);
}

/// <summary>
/// Gets the hierarchical path of a GameObject.
/// </summary>
Expand Down
53 changes: 37 additions & 16 deletions MCPForUnity/Editor/Tools/GameObjects/ManageGameObjectCommon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using MCPForUnity.Editor.Helpers;
using MCPForUnity.Editor.Tools;
using Newtonsoft.Json.Linq;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;

Expand Down Expand Up @@ -83,11 +84,34 @@ internal static List<GameObject> 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":
Expand Down Expand Up @@ -154,7 +178,12 @@ internal static List<GameObject> 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);
Expand All @@ -180,16 +209,8 @@ internal static List<GameObject> FindObjectsInternal(

private static IEnumerable<GameObject> GetAllSceneObjects(bool includeInactive)
{
var rootObjects = SceneManager.GetActiveScene().GetRootGameObjects();
var allObjects = new List<GameObject>();
foreach (var root in rootObjects)
{
allObjects.AddRange(
root.GetComponentsInChildren<Transform>(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)
Expand Down
28 changes: 25 additions & 3 deletions MCPForUnity/Editor/Tools/ManageScene.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
Expand Down