Skip to content

Commit 97b8574

Browse files
authored
[FEATURE] Camera Capture (#449)
* Updates on Camera Capture Feature * Enable Camera Capture through both play and editor mode Notes: Because the standard ScreenCapture.CaptureScreenshot does not work in editor mode, so we use ScreenCapture.CaptureScreenshotIntoRenderTexture to enable it during play mode. * The user can access the camera access through the tool menu or through direct LLM calling. Both tested on Windows with Claude Desktop. * Minor changes nitpicking changes
1 parent 8a17cde commit 97b8574

File tree

6 files changed

+707
-393
lines changed

6 files changed

+707
-393
lines changed

MCPForUnity/Editor/Tools/ManageScene.cs

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.IO;
44
using System.Linq;
55
using MCPForUnity.Editor.Helpers; // For Response class
6+
using MCPForUnity.Runtime.Helpers; // For ScreenshotUtility
67
using Newtonsoft.Json.Linq;
78
using UnityEditor;
89
using UnityEditor.SceneManagement;
@@ -23,6 +24,8 @@ private sealed class SceneCommand
2324
public string name { get; set; } = string.Empty;
2425
public string path { get; set; } = string.Empty;
2526
public int? buildIndex { get; set; }
27+
public string fileName { get; set; } = string.Empty;
28+
public int? superSize { get; set; }
2629
}
2730

2831
private static SceneCommand ToSceneCommand(JObject p)
@@ -42,7 +45,9 @@ private static SceneCommand ToSceneCommand(JObject p)
4245
action = (p["action"]?.ToString() ?? string.Empty).Trim().ToLowerInvariant(),
4346
name = p["name"]?.ToString() ?? string.Empty,
4447
path = p["path"]?.ToString() ?? string.Empty,
45-
buildIndex = BI(p["buildIndex"] ?? p["build_index"])
48+
buildIndex = BI(p["buildIndex"] ?? p["build_index"]),
49+
fileName = (p["fileName"] ?? p["filename"])?.ToString() ?? string.Empty,
50+
superSize = BI(p["superSize"] ?? p["super_size"] ?? p["supersize"])
4651
};
4752
}
4853

@@ -142,14 +147,26 @@ public static object HandleCommand(JObject @params)
142147
return ga;
143148
case "get_build_settings":
144149
return GetBuildSettingsScenes();
150+
case "screenshot":
151+
return CaptureScreenshot(cmd.fileName, cmd.superSize);
145152
// Add cases for modifying build settings, additive loading, unloading etc.
146153
default:
147154
return new ErrorResponse(
148-
$"Unknown action: '{action}'. Valid actions: create, load, save, get_hierarchy, get_active, get_build_settings."
155+
$"Unknown action: '{action}'. Valid actions: create, load, save, get_hierarchy, get_active, get_build_settings, screenshot."
149156
);
150157
}
151158
}
152159

160+
/// <summary>
161+
/// Captures a screenshot to Assets/Screenshots and returns a response payload.
162+
/// Public so the tools UI can reuse the same logic without duplicating parameters.
163+
/// Available in both Edit Mode and Play Mode.
164+
/// </summary>
165+
public static object ExecuteScreenshot(string fileName = null, int? superSize = null)
166+
{
167+
return CaptureScreenshot(fileName, superSize);
168+
}
169+
153170
private static object CreateScene(string fullPath, string relativePath)
154171
{
155172
if (File.Exists(fullPath))
@@ -329,6 +346,55 @@ private static object SaveScene(string fullPath, string relativePath)
329346
}
330347
}
331348

349+
private static object CaptureScreenshot(string fileName, int? superSize)
350+
{
351+
try
352+
{
353+
int resolvedSuperSize = (superSize.HasValue && superSize.Value > 0) ? superSize.Value : 1;
354+
ScreenshotCaptureResult result;
355+
356+
if (Application.isPlaying)
357+
{
358+
result = ScreenshotUtility.CaptureToAssetsFolder(fileName, resolvedSuperSize, ensureUniqueFileName: true);
359+
}
360+
else
361+
{
362+
// Edit Mode path: render from the best-guess camera using RenderTexture.
363+
Camera cam = Camera.main;
364+
if (cam == null)
365+
{
366+
var cams = UnityEngine.Object.FindObjectsOfType<Camera>();
367+
cam = cams.FirstOrDefault();
368+
}
369+
370+
if (cam == null)
371+
{
372+
return new ErrorResponse("No camera found to capture screenshot in Edit Mode.");
373+
}
374+
375+
result = ScreenshotUtility.CaptureFromCameraToAssetsFolder(cam, fileName, resolvedSuperSize, ensureUniqueFileName: true);
376+
}
377+
378+
AssetDatabase.Refresh();
379+
380+
string message = $"Screenshot captured to '{result.AssetsRelativePath}' (full: {result.FullPath}).";
381+
382+
return new SuccessResponse(
383+
message,
384+
new
385+
{
386+
path = result.AssetsRelativePath,
387+
fullPath = result.FullPath,
388+
superSize = result.SuperSize,
389+
}
390+
);
391+
}
392+
catch (Exception e)
393+
{
394+
return new ErrorResponse($"Error capturing screenshot: {e.Message}");
395+
}
396+
}
397+
332398
private static object GetActiveSceneInfo()
333399
{
334400
try

MCPForUnity/Editor/Windows/Components/Tools/McpToolsSection.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using MCPForUnity.Editor.Constants;
55
using MCPForUnity.Editor.Helpers;
66
using MCPForUnity.Editor.Services;
7+
using MCPForUnity.Editor.Tools;
78
using UnityEditor;
89
using UnityEngine.UIElements;
910

@@ -199,6 +200,11 @@ private VisualElement CreateToolRow(ToolMetadata tool)
199200
row.Add(parametersLabel);
200201
}
201202

203+
if (IsManageSceneTool(tool))
204+
{
205+
row.Add(CreateManageSceneActions());
206+
}
207+
202208
return row;
203209
}
204210

@@ -258,13 +264,56 @@ private void AddInfoLabel(string message)
258264
categoryContainer?.Add(label);
259265
}
260266

267+
private VisualElement CreateManageSceneActions()
268+
{
269+
var actions = new VisualElement();
270+
actions.AddToClassList("tool-item-actions");
271+
272+
var screenshotButton = new Button(OnManageSceneScreenshotClicked)
273+
{
274+
text = "Capture Screenshot"
275+
};
276+
screenshotButton.AddToClassList("tool-action-button");
277+
screenshotButton.style.marginTop = 4;
278+
screenshotButton.tooltip = "Capture a screenshot to Assets/Screenshots via manage_scene.";
279+
280+
actions.Add(screenshotButton);
281+
return actions;
282+
}
283+
284+
private void OnManageSceneScreenshotClicked()
285+
{
286+
try
287+
{
288+
var response = ManageScene.ExecuteScreenshot();
289+
if (response is SuccessResponse success && !string.IsNullOrWhiteSpace(success.Message))
290+
{
291+
McpLog.Info(success.Message);
292+
}
293+
else if (response is ErrorResponse error && !string.IsNullOrWhiteSpace(error.Error))
294+
{
295+
McpLog.Error(error.Error);
296+
}
297+
else
298+
{
299+
McpLog.Info("Screenshot capture requested.");
300+
}
301+
}
302+
catch (Exception ex)
303+
{
304+
McpLog.Error($"Failed to capture screenshot: {ex.Message}");
305+
}
306+
}
307+
261308
private static Label CreateTag(string text)
262309
{
263310
var tag = new Label(text);
264311
tag.AddToClassList("tool-tag");
265312
return tag;
266313
}
267314

315+
private static bool IsManageSceneTool(ToolMetadata tool) => string.Equals(tool?.Name, "manage_scene", StringComparison.OrdinalIgnoreCase);
316+
268317
private static bool IsBuiltIn(ToolMetadata tool) => tool?.IsBuiltIn ?? false;
269318
}
270319
}

0 commit comments

Comments
 (0)