@akiojin/unity-mcp-server 2.14.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +206 -0
- package/bin/unity-mcp-server +2 -0
- package/package.json +73 -0
- package/src/core/codeIndex.js +163 -0
- package/src/core/codeIndexDb.js +96 -0
- package/src/core/config.js +165 -0
- package/src/core/indexWatcher.js +52 -0
- package/src/core/projectInfo.js +111 -0
- package/src/core/server.js +294 -0
- package/src/core/unityConnection.js +426 -0
- package/src/handlers/analysis/AnalyzeSceneContentsToolHandler.js +35 -0
- package/src/handlers/analysis/FindByComponentToolHandler.js +20 -0
- package/src/handlers/analysis/GetAnimatorStateToolHandler.js +37 -0
- package/src/handlers/analysis/GetComponentValuesToolHandler.js +20 -0
- package/src/handlers/analysis/GetGameObjectDetailsToolHandler.js +35 -0
- package/src/handlers/analysis/GetInputActionsStateToolHandler.js +37 -0
- package/src/handlers/analysis/GetObjectReferencesToolHandler.js +20 -0
- package/src/handlers/asset/AssetDatabaseToolHandler.js +221 -0
- package/src/handlers/asset/AssetDependencyToolHandler.js +201 -0
- package/src/handlers/asset/AssetImportSettingsToolHandler.js +170 -0
- package/src/handlers/asset/CreateMaterialToolHandler.js +96 -0
- package/src/handlers/asset/CreatePrefabToolHandler.js +78 -0
- package/src/handlers/asset/ExitPrefabModeToolHandler.js +83 -0
- package/src/handlers/asset/InstantiatePrefabToolHandler.js +133 -0
- package/src/handlers/asset/ModifyMaterialToolHandler.js +76 -0
- package/src/handlers/asset/ModifyPrefabToolHandler.js +72 -0
- package/src/handlers/asset/OpenPrefabToolHandler.js +121 -0
- package/src/handlers/asset/SavePrefabToolHandler.js +106 -0
- package/src/handlers/base/BaseToolHandler.js +133 -0
- package/src/handlers/compilation/GetCompilationStateToolHandler.js +90 -0
- package/src/handlers/component/AddComponentToolHandler.js +126 -0
- package/src/handlers/component/GetComponentTypesToolHandler.js +100 -0
- package/src/handlers/component/ListComponentsToolHandler.js +85 -0
- package/src/handlers/component/ModifyComponentToolHandler.js +143 -0
- package/src/handlers/component/RemoveComponentToolHandler.js +108 -0
- package/src/handlers/console/ClearConsoleToolHandler.js +160 -0
- package/src/handlers/console/ReadConsoleToolHandler.js +276 -0
- package/src/handlers/editor/LayerManagementToolHandler.js +160 -0
- package/src/handlers/editor/SelectionToolHandler.js +141 -0
- package/src/handlers/editor/TagManagementToolHandler.js +129 -0
- package/src/handlers/editor/ToolManagementToolHandler.js +135 -0
- package/src/handlers/editor/WindowManagementToolHandler.js +125 -0
- package/src/handlers/gameobject/CreateGameObjectToolHandler.js +131 -0
- package/src/handlers/gameobject/DeleteGameObjectToolHandler.js +101 -0
- package/src/handlers/gameobject/FindGameObjectToolHandler.js +119 -0
- package/src/handlers/gameobject/GetHierarchyToolHandler.js +132 -0
- package/src/handlers/gameobject/ModifyGameObjectToolHandler.js +128 -0
- package/src/handlers/index.js +389 -0
- package/src/handlers/input/AddInputActionToolHandler.js +20 -0
- package/src/handlers/input/AddInputBindingToolHandler.js +20 -0
- package/src/handlers/input/CreateActionMapToolHandler.js +20 -0
- package/src/handlers/input/CreateCompositeBindingToolHandler.js +20 -0
- package/src/handlers/input/GamepadSimulationHandler.js +116 -0
- package/src/handlers/input/InputSystemHandler.js +80 -0
- package/src/handlers/input/KeyboardSimulationHandler.js +79 -0
- package/src/handlers/input/ManageControlSchemesToolHandler.js +20 -0
- package/src/handlers/input/MouseSimulationHandler.js +107 -0
- package/src/handlers/input/RemoveActionMapToolHandler.js +20 -0
- package/src/handlers/input/RemoveAllBindingsToolHandler.js +20 -0
- package/src/handlers/input/RemoveInputActionToolHandler.js +20 -0
- package/src/handlers/input/RemoveInputBindingToolHandler.js +20 -0
- package/src/handlers/input/TouchSimulationHandler.js +142 -0
- package/src/handlers/menu/ExecuteMenuItemToolHandler.js +304 -0
- package/src/handlers/package/PackageManagerToolHandler.js +248 -0
- package/src/handlers/package/RegistryConfigToolHandler.js +198 -0
- package/src/handlers/playmode/GetEditorStateToolHandler.js +81 -0
- package/src/handlers/playmode/PauseToolHandler.js +44 -0
- package/src/handlers/playmode/PlayToolHandler.js +91 -0
- package/src/handlers/playmode/StopToolHandler.js +77 -0
- package/src/handlers/playmode/WaitForEditorStateToolHandler.js +45 -0
- package/src/handlers/scene/CreateSceneToolHandler.js +91 -0
- package/src/handlers/scene/GetSceneInfoToolHandler.js +20 -0
- package/src/handlers/scene/ListScenesToolHandler.js +58 -0
- package/src/handlers/scene/LoadSceneToolHandler.js +92 -0
- package/src/handlers/scene/SaveSceneToolHandler.js +76 -0
- package/src/handlers/screenshot/AnalyzeScreenshotToolHandler.js +238 -0
- package/src/handlers/screenshot/CaptureScreenshotToolHandler.js +692 -0
- package/src/handlers/script/BuildCodeIndexToolHandler.js +163 -0
- package/src/handlers/script/ScriptCreateClassFileToolHandler.js +60 -0
- package/src/handlers/script/ScriptEditStructuredToolHandler.js +173 -0
- package/src/handlers/script/ScriptIndexStatusToolHandler.js +61 -0
- package/src/handlers/script/ScriptPackagesListToolHandler.js +103 -0
- package/src/handlers/script/ScriptReadToolHandler.js +106 -0
- package/src/handlers/script/ScriptRefactorRenameToolHandler.js +83 -0
- package/src/handlers/script/ScriptRefsFindToolHandler.js +144 -0
- package/src/handlers/script/ScriptRemoveSymbolToolHandler.js +79 -0
- package/src/handlers/script/ScriptSearchToolHandler.js +320 -0
- package/src/handlers/script/ScriptSymbolFindToolHandler.js +117 -0
- package/src/handlers/script/ScriptSymbolsGetToolHandler.js +96 -0
- package/src/handlers/settings/GetProjectSettingsToolHandler.js +161 -0
- package/src/handlers/settings/UpdateProjectSettingsToolHandler.js +272 -0
- package/src/handlers/system/GetCommandStatsToolHandler.js +25 -0
- package/src/handlers/system/PingToolHandler.js +53 -0
- package/src/handlers/system/RefreshAssetsToolHandler.js +45 -0
- package/src/handlers/ui/ClickUIElementToolHandler.js +110 -0
- package/src/handlers/ui/FindUIElementsToolHandler.js +63 -0
- package/src/handlers/ui/GetUIElementStateToolHandler.js +50 -0
- package/src/handlers/ui/SetUIElementValueToolHandler.js +49 -0
- package/src/handlers/ui/SimulateUIInputToolHandler.js +156 -0
- package/src/handlers/video/CaptureVideoForToolHandler.js +96 -0
- package/src/handlers/video/CaptureVideoStartToolHandler.js +38 -0
- package/src/handlers/video/CaptureVideoStatusToolHandler.js +30 -0
- package/src/handlers/video/CaptureVideoStopToolHandler.js +32 -0
- package/src/lsp/CSharpLspUtils.js +134 -0
- package/src/lsp/LspProcessManager.js +60 -0
- package/src/lsp/LspRpcClient.js +133 -0
- package/src/tools/analysis/analyzeSceneContents.js +100 -0
- package/src/tools/analysis/findByComponent.js +87 -0
- package/src/tools/analysis/getAnimatorState.js +326 -0
- package/src/tools/analysis/getComponentValues.js +182 -0
- package/src/tools/analysis/getGameObjectDetails.js +159 -0
- package/src/tools/analysis/getInputActionsState.js +329 -0
- package/src/tools/analysis/getObjectReferences.js +86 -0
- package/src/tools/input/inputActionsEditor.js +556 -0
- package/src/tools/scene/createScene.js +112 -0
- package/src/tools/scene/getSceneInfo.js +95 -0
- package/src/tools/scene/listScenes.js +82 -0
- package/src/tools/scene/loadScene.js +122 -0
- package/src/tools/scene/saveScene.js +91 -0
- package/src/tools/system/ping.js +72 -0
- package/src/tools/video/recordFor.js +31 -0
- package/src/tools/video/recordPlayMode.js +61 -0
- package/src/utils/csharpParse.js +88 -0
- package/src/utils/validators.js +90 -0
|
@@ -0,0 +1,692 @@
|
|
|
1
|
+
import { BaseToolHandler } from '../base/BaseToolHandler.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handler for capturing screenshots from Unity Editor
|
|
5
|
+
*/
|
|
6
|
+
export class CaptureScreenshotToolHandler extends BaseToolHandler {
|
|
7
|
+
constructor(unityConnection) {
|
|
8
|
+
super(
|
|
9
|
+
'capture_screenshot',
|
|
10
|
+
'Capture Game/Scene/Window/Explorer screenshots. Output path is fixed to <workspace>/.unity/capture/. For LLM use, prefer explorer mode (auto-framing, clarity). Use encodeAsBase64=true only for immediate analysis, and keep resolution minimal.',
|
|
11
|
+
{
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
captureMode: {
|
|
15
|
+
type: 'string',
|
|
16
|
+
enum: ['game', 'scene', 'window', 'explorer'],
|
|
17
|
+
description: 'Capture mode selection:\n- "game": Game View (player\'s perspective, what the user sees)\n- "scene": Scene View (editor\'s 3D workspace view)\n- "explorer": AI/LLM exploration mode (optimized view for understanding scene content)\n- "window": Specific Unity Editor window by name'
|
|
18
|
+
},
|
|
19
|
+
width: {
|
|
20
|
+
type: 'number',
|
|
21
|
+
description: 'Screenshot width in pixels (0 = use current view size). Range: 1-8192. Not applicable for explorer mode (use explorerSettings.camera.width instead)'
|
|
22
|
+
},
|
|
23
|
+
height: {
|
|
24
|
+
type: 'number',
|
|
25
|
+
description: 'Screenshot height in pixels (0 = use current view size). Range: 1-8192. Not applicable for explorer mode (use explorerSettings.camera.height instead)'
|
|
26
|
+
},
|
|
27
|
+
includeUI: {
|
|
28
|
+
type: 'boolean',
|
|
29
|
+
description: 'Whether to include UI overlay elements in Game View captures. Set to false for clean gameplay screenshots (default: true)'
|
|
30
|
+
},
|
|
31
|
+
windowName: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
description: 'Name of the Unity Editor window to capture (required when captureMode is "window"). Examples: "Console", "Inspector", "Hierarchy", "Project"'
|
|
34
|
+
},
|
|
35
|
+
encodeAsBase64: {
|
|
36
|
+
type: 'boolean',
|
|
37
|
+
description: 'Return screenshot data as base64 string for immediate processing/analysis without file I/O (default: false)'
|
|
38
|
+
},
|
|
39
|
+
explorerSettings: {
|
|
40
|
+
type: 'object',
|
|
41
|
+
description: 'Configuration for explorer mode - AI/LLM optimized capture settings',
|
|
42
|
+
properties: {
|
|
43
|
+
target: {
|
|
44
|
+
type: 'object',
|
|
45
|
+
description: 'Defines what to focus on in the scene',
|
|
46
|
+
properties: {
|
|
47
|
+
type: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
enum: ['gameObject', 'tag', 'area', 'position'],
|
|
50
|
+
description: 'Target selection method:\n- "gameObject": Focus on a specific GameObject by name\n- "tag": Focus on all objects with a specific tag\n- "area": Focus on a circular area defined by center and radius\n- "position": Manual camera positioning without auto-framing'
|
|
51
|
+
},
|
|
52
|
+
name: {
|
|
53
|
+
type: 'string',
|
|
54
|
+
description: 'GameObject name to target (required for "gameObject" type). Example: "Player", "MainCamera", "Enemy_01"'
|
|
55
|
+
},
|
|
56
|
+
tag: {
|
|
57
|
+
type: 'string',
|
|
58
|
+
description: 'Tag to search for GameObjects (required for "tag" type). Example: "Enemy", "Collectible", "Player"'
|
|
59
|
+
},
|
|
60
|
+
center: {
|
|
61
|
+
type: 'object',
|
|
62
|
+
description: 'Center position for area targeting (required for "area" type)',
|
|
63
|
+
properties: {
|
|
64
|
+
x: { type: 'number', description: 'X coordinate in world space' },
|
|
65
|
+
y: { type: 'number', description: 'Y coordinate in world space' },
|
|
66
|
+
z: { type: 'number', description: 'Z coordinate in world space' }
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
radius: {
|
|
70
|
+
type: 'number',
|
|
71
|
+
description: 'Radius around center for area targeting (used with "area" type). Defines the circular area to capture'
|
|
72
|
+
},
|
|
73
|
+
includeChildren: {
|
|
74
|
+
type: 'boolean',
|
|
75
|
+
description: 'Include child objects when calculating the target bounds for framing (default: true)'
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
camera: {
|
|
80
|
+
type: 'object',
|
|
81
|
+
description: 'Camera configuration for the capture',
|
|
82
|
+
properties: {
|
|
83
|
+
position: {
|
|
84
|
+
type: 'object',
|
|
85
|
+
description: 'Manual camera position in world space (overrides auto-framing)',
|
|
86
|
+
properties: {
|
|
87
|
+
x: { type: 'number', description: 'X position in world units' },
|
|
88
|
+
y: { type: 'number', description: 'Y position in world units' },
|
|
89
|
+
z: { type: 'number', description: 'Z position in world units' }
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
lookAt: {
|
|
93
|
+
type: 'object',
|
|
94
|
+
description: 'Point for camera to look at in world space',
|
|
95
|
+
properties: {
|
|
96
|
+
x: { type: 'number', description: 'X coordinate to look at' },
|
|
97
|
+
y: { type: 'number', description: 'Y coordinate to look at' },
|
|
98
|
+
z: { type: 'number', description: 'Z coordinate to look at' }
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
rotation: {
|
|
102
|
+
type: 'object',
|
|
103
|
+
description: 'Camera rotation in Euler angles (alternative to lookAt)',
|
|
104
|
+
properties: {
|
|
105
|
+
x: { type: 'number', description: 'X rotation (pitch) in degrees' },
|
|
106
|
+
y: { type: 'number', description: 'Y rotation (yaw) in degrees' },
|
|
107
|
+
z: { type: 'number', description: 'Z rotation (roll) in degrees' }
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
fieldOfView: {
|
|
111
|
+
type: 'number',
|
|
112
|
+
|
|
113
|
+
description: 'Camera field of view in degrees (1-179). Lower values zoom in, higher values zoom out'
|
|
114
|
+
},
|
|
115
|
+
nearClip: {
|
|
116
|
+
type: 'number',
|
|
117
|
+
description: 'Near clipping plane distance. Objects closer than this won\'t be rendered (default: 0.3)'
|
|
118
|
+
},
|
|
119
|
+
farClip: {
|
|
120
|
+
type: 'number',
|
|
121
|
+
description: 'Far clipping plane distance. Objects farther than this won\'t be rendered'
|
|
122
|
+
},
|
|
123
|
+
autoFrame: {
|
|
124
|
+
type: 'boolean',
|
|
125
|
+
description: 'Automatically position camera to frame the target. Disable for manual positioning (default: true)'
|
|
126
|
+
},
|
|
127
|
+
padding: {
|
|
128
|
+
type: 'number',
|
|
129
|
+
description: 'Padding around target when auto-framing (0-1). 0 = tight fit, 1 = lots of space (default: 0.2)'
|
|
130
|
+
},
|
|
131
|
+
offset: {
|
|
132
|
+
type: 'object',
|
|
133
|
+
description: 'Additional offset from calculated auto-frame position',
|
|
134
|
+
properties: {
|
|
135
|
+
x: { type: 'number', description: 'X offset in world units' },
|
|
136
|
+
y: { type: 'number', description: 'Y offset in world units' },
|
|
137
|
+
z: { type: 'number', description: 'Z offset in world units' }
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
width: {
|
|
141
|
+
type: 'number',
|
|
142
|
+
description: 'Capture resolution width in pixels (1-8192)'
|
|
143
|
+
},
|
|
144
|
+
height: {
|
|
145
|
+
type: 'number',
|
|
146
|
+
description: 'Capture resolution height in pixels (1-8192)'
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
display: {
|
|
151
|
+
type: 'object',
|
|
152
|
+
description: 'Visual display settings for the capture',
|
|
153
|
+
properties: {
|
|
154
|
+
backgroundColor: {
|
|
155
|
+
type: 'object',
|
|
156
|
+
description: 'Background color for the capture (RGBA values 0-1)',
|
|
157
|
+
properties: {
|
|
158
|
+
r: { type: 'number', description: 'Red component (0-1, default: 0.2)' },
|
|
159
|
+
g: { type: 'number', description: 'Green component (0-1, default: 0.2)' },
|
|
160
|
+
b: { type: 'number', description: 'Blue component (0-1, default: 0.2)' },
|
|
161
|
+
a: { type: 'number', description: 'Alpha/opacity (0-1, default: 1)' }
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
layers: {
|
|
165
|
+
type: 'array',
|
|
166
|
+
items: { type: 'string' },
|
|
167
|
+
description: 'Unity layer names to include in rendering. If not specified, all layers are included. Examples: ["Default", "UI", "Player"]'
|
|
168
|
+
},
|
|
169
|
+
showGizmos: {
|
|
170
|
+
type: 'boolean',
|
|
171
|
+
description: 'Show editor gizmos (transform handles, icons) in the capture (default: false)'
|
|
172
|
+
},
|
|
173
|
+
showColliders: {
|
|
174
|
+
type: 'boolean',
|
|
175
|
+
description: 'Visualize physics colliders as wireframes (default: false)'
|
|
176
|
+
},
|
|
177
|
+
showBounds: {
|
|
178
|
+
type: 'boolean',
|
|
179
|
+
description: 'Show bounding boxes around objects (default: false)'
|
|
180
|
+
},
|
|
181
|
+
highlightTarget: {
|
|
182
|
+
type: 'boolean',
|
|
183
|
+
description: 'Highlight the target object(s) with an outline or color (default: false)'
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
required: []
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
this.unityConnection = unityConnection;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Validates the input parameters
|
|
199
|
+
* @param {Object} params - The input parameters
|
|
200
|
+
* @throws {Error} If validation fails
|
|
201
|
+
*/
|
|
202
|
+
validate(params) {
|
|
203
|
+
const { captureMode = 'game', windowName, outputPath, explorerSettings } = params;
|
|
204
|
+
|
|
205
|
+
// Validate capture mode
|
|
206
|
+
if (!['game', 'scene', 'window', 'explorer'].includes(captureMode)) {
|
|
207
|
+
throw new Error(
|
|
208
|
+
'Invalid captureMode. Must be one of: "game" (Game View), "scene" (Scene View), "window" (specific editor window), or "explorer" (AI/LLM exploration mode)'
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Window mode requires windowName
|
|
213
|
+
if (captureMode === 'window' && !windowName) {
|
|
214
|
+
throw new Error(
|
|
215
|
+
'windowName is required when captureMode is "window". ' +
|
|
216
|
+
'Specify the name of the Unity Editor window to capture (e.g., "Console", "Inspector", "Hierarchy")'
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Explorer mode validation
|
|
221
|
+
if (captureMode === 'explorer' && explorerSettings) {
|
|
222
|
+
const { target, camera, display } = explorerSettings;
|
|
223
|
+
|
|
224
|
+
// Validate target settings
|
|
225
|
+
if (target) {
|
|
226
|
+
const { type } = target;
|
|
227
|
+
if (type && !['gameObject', 'tag', 'area', 'position'].includes(type)) {
|
|
228
|
+
throw new Error(
|
|
229
|
+
'Invalid target.type for explorer mode. Must be one of:\n' +
|
|
230
|
+
'- "gameObject": Focus on a specific GameObject by name\n' +
|
|
231
|
+
'- "tag": Focus on all objects with a specific tag\n' +
|
|
232
|
+
'- "area": Focus on a circular area (center + radius)\n' +
|
|
233
|
+
'- "position": Manual camera positioning'
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (type === 'gameObject' && !target.name) {
|
|
238
|
+
throw new Error(
|
|
239
|
+
'target.name is required when target.type is "gameObject". ' +
|
|
240
|
+
'Specify the name of the GameObject to focus on (e.g., "Player", "MainCamera")'
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (type === 'tag' && !target.tag) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
'target.tag is required when target.type is "tag". ' +
|
|
247
|
+
'Specify the tag to search for (e.g., "Enemy", "Collectible")'
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (type === 'area') {
|
|
252
|
+
if (!target.center) {
|
|
253
|
+
throw new Error(
|
|
254
|
+
'target.center is required when target.type is "area". ' +
|
|
255
|
+
'Specify the center position as {x, y, z} in world coordinates'
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
if (target.radius === undefined || target.radius <= 0) {
|
|
259
|
+
throw new Error(
|
|
260
|
+
'target.radius must be a positive number when target.type is "area". ' +
|
|
261
|
+
'Specify the radius of the area to capture'
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Validate camera settings
|
|
268
|
+
if (camera) {
|
|
269
|
+
if (camera.fieldOfView !== undefined) {
|
|
270
|
+
if (camera.fieldOfView < 1 || camera.fieldOfView > 179) {
|
|
271
|
+
throw new Error(
|
|
272
|
+
'camera.fieldOfView must be between 1 and 179 degrees. ' +
|
|
273
|
+
'Lower values (20-40) provide zoom effect, standard is 60, wide angle is 90+'
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (camera.padding !== undefined) {
|
|
279
|
+
if (camera.padding < 0 || camera.padding > 1) {
|
|
280
|
+
throw new Error(
|
|
281
|
+
'camera.padding must be between 0 and 1. ' +
|
|
282
|
+
'0 = tight framing, 0.2 = normal padding, 0.5+ = lots of surrounding space'
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (camera.width !== undefined) {
|
|
288
|
+
if (camera.width < 1 || camera.width > 8192) {
|
|
289
|
+
throw new Error(
|
|
290
|
+
'camera.width must be between 1 and 8192 pixels. ' +
|
|
291
|
+
'Common values: 1920 (Full HD), 2560 (2K), 3840 (4K)'
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (camera.height !== undefined) {
|
|
297
|
+
if (camera.height < 1 || camera.height > 8192) {
|
|
298
|
+
throw new Error(
|
|
299
|
+
'camera.height must be between 1 and 8192 pixels. ' +
|
|
300
|
+
'Common values: 1080 (Full HD), 1440 (2K), 2160 (4K)'
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (camera.nearClip !== undefined && camera.nearClip <= 0) {
|
|
306
|
+
throw new Error(
|
|
307
|
+
'camera.nearClip must be a positive number. ' +
|
|
308
|
+
'Typically 0.01 to 1.0 for close-up views, 0.3 is default'
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (camera.farClip !== undefined && camera.farClip <= 0) {
|
|
313
|
+
throw new Error(
|
|
314
|
+
'camera.farClip must be a positive number. ' +
|
|
315
|
+
'Should be larger than nearClip. Default is 1000, reduce for performance'
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (camera.nearClip !== undefined && camera.farClip !== undefined) {
|
|
320
|
+
if (camera.nearClip >= camera.farClip) {
|
|
321
|
+
throw new Error(
|
|
322
|
+
'camera.nearClip must be less than camera.farClip. ' +
|
|
323
|
+
'nearClip defines the closest visible distance, farClip the farthest'
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Validate display settings
|
|
330
|
+
if (display) {
|
|
331
|
+
if (display.backgroundColor) {
|
|
332
|
+
const { r, g, b, a } = display.backgroundColor;
|
|
333
|
+
const validateColorComponent = (value, name) => {
|
|
334
|
+
if (value !== undefined && (value < 0 || value > 1)) {
|
|
335
|
+
throw new Error(
|
|
336
|
+
`backgroundColor.${name} must be between 0 and 1. ` +
|
|
337
|
+
'Color components use normalized values (0 = black/transparent, 1 = full intensity)'
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
validateColorComponent(r, 'r');
|
|
342
|
+
validateColorComponent(g, 'g');
|
|
343
|
+
validateColorComponent(b, 'b');
|
|
344
|
+
validateColorComponent(a, 'a');
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (display.layers && !Array.isArray(display.layers)) {
|
|
348
|
+
throw new Error(
|
|
349
|
+
'display.layers must be an array of layer names. ' +
|
|
350
|
+
'Example: ["Default", "UI", "Player"]'
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Validate output path if provided
|
|
357
|
+
if (outputPath) {
|
|
358
|
+
const validExtensions = ['.png', '.jpg', '.jpeg'];
|
|
359
|
+
const hasValidExtension = validExtensions.some(ext => outputPath.toLowerCase().endsWith(ext));
|
|
360
|
+
|
|
361
|
+
if (!hasValidExtension) {
|
|
362
|
+
throw new Error(
|
|
363
|
+
'outputPath must end with .png, .jpg, or .jpeg. ' +
|
|
364
|
+
'PNG is recommended for best quality, JPG for smaller file size'
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (!outputPath.startsWith('Assets/')) {
|
|
369
|
+
throw new Error(
|
|
370
|
+
'outputPath must be within the Assets folder. ' +
|
|
371
|
+
'Example: "Assets/Screenshots/capture.png"'
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Warn about potential path issues
|
|
376
|
+
if (outputPath.includes('\\')) {
|
|
377
|
+
throw new Error(
|
|
378
|
+
'outputPath should use forward slashes (/) not backslashes (\\). ' +
|
|
379
|
+
'Example: "Assets/Screenshots/capture.png"'
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Validate dimensions for non-explorer modes
|
|
385
|
+
if (captureMode !== 'explorer') {
|
|
386
|
+
if (params.width !== undefined) {
|
|
387
|
+
if (params.width < 0 || params.width > 8192) {
|
|
388
|
+
throw new Error(
|
|
389
|
+
'width must be between 0 and 8192 pixels. ' +
|
|
390
|
+
'0 = use current view size, common values: 1920, 2560, 3840'
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (params.height !== undefined) {
|
|
396
|
+
if (params.height < 0 || params.height > 8192) {
|
|
397
|
+
throw new Error(
|
|
398
|
+
'height must be between 0 and 8192 pixels. ' +
|
|
399
|
+
'0 = use current view size, common values: 1080, 1440, 2160'
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
} else if (params.width !== undefined || params.height !== undefined) {
|
|
404
|
+
throw new Error(
|
|
405
|
+
'For explorer mode, set resolution using explorerSettings.camera.width and explorerSettings.camera.height instead of top-level width/height'
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Executes the screenshot capture
|
|
412
|
+
* @param {Object} params - The validated input parameters
|
|
413
|
+
* @returns {Promise<Object>} The screenshot capture result
|
|
414
|
+
*/
|
|
415
|
+
async execute(params) {
|
|
416
|
+
// Ensure connection to Unity
|
|
417
|
+
if (!this.unityConnection.isConnected()) {
|
|
418
|
+
await this.unityConnection.connect();
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Send capture command to Unity
|
|
422
|
+
const { WORKSPACE_ROOT } = await import('../../core/config.js');
|
|
423
|
+
const response = await this.unityConnection.sendCommand('capture_screenshot', { ...(params||{}), workspaceRoot: WORKSPACE_ROOT });
|
|
424
|
+
|
|
425
|
+
// Handle Unity response
|
|
426
|
+
if (response.error) {
|
|
427
|
+
throw new Error(response.error);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Build result object
|
|
431
|
+
const result = {
|
|
432
|
+
path: response.path,
|
|
433
|
+
width: response.width,
|
|
434
|
+
height: response.height,
|
|
435
|
+
captureMode: response.captureMode,
|
|
436
|
+
fileSize: response.fileSize,
|
|
437
|
+
message: response.message || 'Screenshot captured successfully'
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
// Include optional fields if present
|
|
441
|
+
if (response.includeUI !== undefined) {
|
|
442
|
+
result.includeUI = response.includeUI;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (response.cameraPosition) {
|
|
446
|
+
result.cameraPosition = response.cameraPosition;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (response.cameraRotation) {
|
|
450
|
+
result.cameraRotation = response.cameraRotation;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (response.base64Data) {
|
|
454
|
+
result.base64Data = response.base64Data;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return result;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Gets example usage for this tool
|
|
462
|
+
* @returns {Object} Example usage scenarios
|
|
463
|
+
*/
|
|
464
|
+
getExamples() {
|
|
465
|
+
return {
|
|
466
|
+
// Basic capture examples
|
|
467
|
+
quickGameCapture: {
|
|
468
|
+
description: 'Quick capture of current game view for debugging',
|
|
469
|
+
params: {
|
|
470
|
+
captureMode: 'game'
|
|
471
|
+
}
|
|
472
|
+
},
|
|
473
|
+
captureForAnalysis: {
|
|
474
|
+
description: 'Capture game view as base64 for immediate AI analysis',
|
|
475
|
+
params: {
|
|
476
|
+
captureMode: 'game',
|
|
477
|
+
encodeAsBase64: true,
|
|
478
|
+
includeUI: false
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
highResSceneCapture: {
|
|
482
|
+
description: 'High-resolution scene view capture for documentation',
|
|
483
|
+
params: {
|
|
484
|
+
captureMode: 'scene',
|
|
485
|
+
width: 3840,
|
|
486
|
+
height: 2160,
|
|
487
|
+
outputPath: 'Assets/Screenshots/scene_4k.png'
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
|
|
491
|
+
// Explorer mode examples for AI/LLM usage
|
|
492
|
+
analyzePlayer: {
|
|
493
|
+
description: 'AI/LLM: Analyze the player character and its surroundings',
|
|
494
|
+
params: {
|
|
495
|
+
captureMode: 'explorer',
|
|
496
|
+
encodeAsBase64: true,
|
|
497
|
+
explorerSettings: {
|
|
498
|
+
target: {
|
|
499
|
+
type: 'gameObject',
|
|
500
|
+
name: 'Player',
|
|
501
|
+
includeChildren: true
|
|
502
|
+
},
|
|
503
|
+
camera: {
|
|
504
|
+
autoFrame: true,
|
|
505
|
+
padding: 0.3,
|
|
506
|
+
fieldOfView: 45
|
|
507
|
+
},
|
|
508
|
+
display: {
|
|
509
|
+
backgroundColor: { r: 0.15, g: 0.15, b: 0.2, a: 1 },
|
|
510
|
+
highlightTarget: true
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
},
|
|
515
|
+
inspectUILayout: {
|
|
516
|
+
description: 'AI/LLM: Inspect UI canvas layout and elements',
|
|
517
|
+
params: {
|
|
518
|
+
captureMode: 'explorer',
|
|
519
|
+
encodeAsBase64: true,
|
|
520
|
+
explorerSettings: {
|
|
521
|
+
target: {
|
|
522
|
+
type: 'gameObject',
|
|
523
|
+
name: 'Canvas',
|
|
524
|
+
includeChildren: true
|
|
525
|
+
},
|
|
526
|
+
camera: {
|
|
527
|
+
autoFrame: true,
|
|
528
|
+
padding: 0.1
|
|
529
|
+
},
|
|
530
|
+
display: {
|
|
531
|
+
layers: ['UI'],
|
|
532
|
+
backgroundColor: { r: 0.1, g: 0.1, b: 0.1, a: 1 }
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
},
|
|
537
|
+
findAllEnemies: {
|
|
538
|
+
description: 'AI/LLM: Locate and analyze all enemies in the scene',
|
|
539
|
+
params: {
|
|
540
|
+
captureMode: 'explorer',
|
|
541
|
+
encodeAsBase64: true,
|
|
542
|
+
explorerSettings: {
|
|
543
|
+
target: {
|
|
544
|
+
type: 'tag',
|
|
545
|
+
tag: 'Enemy'
|
|
546
|
+
},
|
|
547
|
+
camera: {
|
|
548
|
+
autoFrame: true,
|
|
549
|
+
padding: 0.4,
|
|
550
|
+
fieldOfView: 60
|
|
551
|
+
},
|
|
552
|
+
display: {
|
|
553
|
+
highlightTarget: true,
|
|
554
|
+
showBounds: true
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
},
|
|
559
|
+
inspectGameArea: {
|
|
560
|
+
description: 'AI/LLM: Overview of a specific game area or zone',
|
|
561
|
+
params: {
|
|
562
|
+
captureMode: 'explorer',
|
|
563
|
+
encodeAsBase64: true,
|
|
564
|
+
explorerSettings: {
|
|
565
|
+
target: {
|
|
566
|
+
type: 'area',
|
|
567
|
+
center: { x: 0, y: 0, z: 0 },
|
|
568
|
+
radius: 30
|
|
569
|
+
},
|
|
570
|
+
camera: {
|
|
571
|
+
position: { x: 20, y: 30, z: -20 },
|
|
572
|
+
lookAt: { x: 0, y: 0, z: 0 },
|
|
573
|
+
fieldOfView: 50
|
|
574
|
+
},
|
|
575
|
+
display: {
|
|
576
|
+
layers: ['Default', 'Terrain', 'Buildings'],
|
|
577
|
+
showColliders: false
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
},
|
|
582
|
+
debugPhysics: {
|
|
583
|
+
description: 'AI/LLM: Debug physics setup with colliders visible',
|
|
584
|
+
params: {
|
|
585
|
+
captureMode: 'explorer',
|
|
586
|
+
encodeAsBase64: true,
|
|
587
|
+
explorerSettings: {
|
|
588
|
+
target: {
|
|
589
|
+
type: 'gameObject',
|
|
590
|
+
name: 'PhysicsTestObject',
|
|
591
|
+
includeChildren: true
|
|
592
|
+
},
|
|
593
|
+
camera: {
|
|
594
|
+
autoFrame: true,
|
|
595
|
+
padding: 0.5
|
|
596
|
+
},
|
|
597
|
+
display: {
|
|
598
|
+
showColliders: true,
|
|
599
|
+
showBounds: true,
|
|
600
|
+
backgroundColor: { r: 0.05, g: 0.05, b: 0.1, a: 1 }
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
birdEyeView: {
|
|
606
|
+
description: 'AI/LLM: Top-down bird\'s eye view of the entire level',
|
|
607
|
+
params: {
|
|
608
|
+
captureMode: 'explorer',
|
|
609
|
+
encodeAsBase64: true,
|
|
610
|
+
explorerSettings: {
|
|
611
|
+
camera: {
|
|
612
|
+
position: { x: 0, y: 100, z: 0 },
|
|
613
|
+
rotation: { x: 90, y: 0, z: 0 },
|
|
614
|
+
fieldOfView: 60,
|
|
615
|
+
farClip: 200
|
|
616
|
+
},
|
|
617
|
+
display: {
|
|
618
|
+
backgroundColor: { r: 0.53, g: 0.81, b: 0.92, a: 1 }
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
},
|
|
623
|
+
|
|
624
|
+
// Window capture examples
|
|
625
|
+
captureConsole: {
|
|
626
|
+
description: 'Capture Unity console for error debugging',
|
|
627
|
+
params: {
|
|
628
|
+
captureMode: 'window',
|
|
629
|
+
windowName: 'Console',
|
|
630
|
+
outputPath: 'Assets/Screenshots/console_errors.png'
|
|
631
|
+
}
|
|
632
|
+
},
|
|
633
|
+
captureInspector: {
|
|
634
|
+
description: 'Capture inspector window to document component settings',
|
|
635
|
+
params: {
|
|
636
|
+
captureMode: 'window',
|
|
637
|
+
windowName: 'Inspector'
|
|
638
|
+
}
|
|
639
|
+
},
|
|
640
|
+
|
|
641
|
+
// Advanced explorer examples
|
|
642
|
+
cinematicShot: {
|
|
643
|
+
description: 'AI/LLM: Cinematic angle shot of a specific scene moment',
|
|
644
|
+
params: {
|
|
645
|
+
captureMode: 'explorer',
|
|
646
|
+
explorerSettings: {
|
|
647
|
+
target: {
|
|
648
|
+
type: 'gameObject',
|
|
649
|
+
name: 'MainCharacter'
|
|
650
|
+
},
|
|
651
|
+
camera: {
|
|
652
|
+
autoFrame: false,
|
|
653
|
+
position: { x: 5, y: 2, z: -8 },
|
|
654
|
+
lookAt: { x: 0, y: 1.5, z: 0 },
|
|
655
|
+
fieldOfView: 35,
|
|
656
|
+
width: 2560,
|
|
657
|
+
height: 1440
|
|
658
|
+
},
|
|
659
|
+
display: {
|
|
660
|
+
backgroundColor: { r: 0.02, g: 0.02, b: 0.05, a: 1 },
|
|
661
|
+
showGizmos: false
|
|
662
|
+
}
|
|
663
|
+
},
|
|
664
|
+
outputPath: 'Assets/Screenshots/cinematic_shot.png'
|
|
665
|
+
}
|
|
666
|
+
},
|
|
667
|
+
multiObjectAnalysis: {
|
|
668
|
+
description: 'AI/LLM: Analyze multiple tagged objects in one view',
|
|
669
|
+
params: {
|
|
670
|
+
captureMode: 'explorer',
|
|
671
|
+
encodeAsBase64: true,
|
|
672
|
+
explorerSettings: {
|
|
673
|
+
target: {
|
|
674
|
+
type: 'tag',
|
|
675
|
+
tag: 'Interactive'
|
|
676
|
+
},
|
|
677
|
+
camera: {
|
|
678
|
+
autoFrame: true,
|
|
679
|
+
padding: 0.5,
|
|
680
|
+
fieldOfView: 70
|
|
681
|
+
},
|
|
682
|
+
display: {
|
|
683
|
+
highlightTarget: true,
|
|
684
|
+
showBounds: true,
|
|
685
|
+
layers: ['Default', 'Interactive']
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
}
|