mcpforunityserver 8.7.0__py3-none-any.whl → 9.1.0__py3-none-any.whl
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.
- cli/__init__.py +3 -0
- cli/commands/__init__.py +3 -0
- cli/commands/animation.py +87 -0
- cli/commands/asset.py +310 -0
- cli/commands/audio.py +133 -0
- cli/commands/batch.py +184 -0
- cli/commands/code.py +189 -0
- cli/commands/component.py +212 -0
- cli/commands/editor.py +487 -0
- cli/commands/gameobject.py +510 -0
- cli/commands/instance.py +101 -0
- cli/commands/lighting.py +128 -0
- cli/commands/material.py +268 -0
- cli/commands/prefab.py +144 -0
- cli/commands/scene.py +255 -0
- cli/commands/script.py +240 -0
- cli/commands/shader.py +238 -0
- cli/commands/ui.py +263 -0
- cli/commands/vfx.py +439 -0
- cli/main.py +248 -0
- cli/utils/__init__.py +31 -0
- cli/utils/config.py +58 -0
- cli/utils/connection.py +191 -0
- cli/utils/output.py +195 -0
- main.py +177 -62
- {mcpforunityserver-8.7.0.dist-info → mcpforunityserver-9.1.0.dist-info}/METADATA +4 -2
- mcpforunityserver-9.1.0.dist-info/RECORD +96 -0
- {mcpforunityserver-8.7.0.dist-info → mcpforunityserver-9.1.0.dist-info}/WHEEL +1 -1
- {mcpforunityserver-8.7.0.dist-info → mcpforunityserver-9.1.0.dist-info}/entry_points.txt +1 -0
- {mcpforunityserver-8.7.0.dist-info → mcpforunityserver-9.1.0.dist-info}/top_level.txt +1 -2
- services/custom_tool_service.py +179 -19
- services/resources/__init__.py +6 -1
- services/resources/active_tool.py +1 -1
- services/resources/custom_tools.py +2 -2
- services/resources/editor_state.py +283 -30
- services/resources/gameobject.py +243 -0
- services/resources/layers.py +1 -1
- services/resources/prefab_stage.py +1 -1
- services/resources/project_info.py +1 -1
- services/resources/selection.py +1 -1
- services/resources/tags.py +1 -1
- services/resources/unity_instances.py +1 -1
- services/resources/windows.py +1 -1
- services/state/external_changes_scanner.py +3 -4
- services/tools/__init__.py +6 -1
- services/tools/batch_execute.py +24 -9
- services/tools/debug_request_context.py +8 -2
- services/tools/execute_custom_tool.py +6 -1
- services/tools/execute_menu_item.py +6 -3
- services/tools/find_gameobjects.py +89 -0
- services/tools/find_in_file.py +26 -19
- services/tools/manage_asset.py +13 -44
- services/tools/manage_components.py +131 -0
- services/tools/manage_editor.py +9 -8
- services/tools/manage_gameobject.py +115 -79
- services/tools/manage_material.py +80 -31
- services/tools/manage_prefabs.py +7 -1
- services/tools/manage_scene.py +30 -13
- services/tools/manage_script.py +62 -19
- services/tools/manage_scriptable_object.py +22 -10
- services/tools/manage_shader.py +8 -1
- services/tools/manage_vfx.py +738 -0
- services/tools/preflight.py +15 -12
- services/tools/read_console.py +70 -17
- services/tools/refresh_unity.py +92 -29
- services/tools/run_tests.py +187 -53
- services/tools/script_apply_edits.py +15 -7
- services/tools/set_active_instance.py +12 -7
- services/tools/utils.py +60 -6
- transport/legacy/port_discovery.py +2 -2
- transport/legacy/unity_connection.py +129 -26
- transport/plugin_hub.py +85 -24
- transport/unity_instance_middleware.py +4 -3
- transport/unity_transport.py +2 -1
- utils/focus_nudge.py +321 -0
- __init__.py +0 -0
- mcpforunityserver-8.7.0.dist-info/RECORD +0 -71
- routes/__init__.py +0 -0
- services/resources/editor_state_v2.py +0 -270
- services/tools/test_jobs.py +0 -94
- {mcpforunityserver-8.7.0.dist-info → mcpforunityserver-9.1.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
"""GameObject CLI commands."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import json
|
|
5
|
+
import click
|
|
6
|
+
from typing import Optional, Tuple, Any
|
|
7
|
+
|
|
8
|
+
from cli.utils.config import get_config
|
|
9
|
+
from cli.utils.output import format_output, print_error, print_success, print_warning
|
|
10
|
+
from cli.utils.connection import run_command, UnityConnectionError
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@click.group()
|
|
14
|
+
def gameobject():
|
|
15
|
+
"""GameObject operations - create, find, modify, delete GameObjects."""
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@gameobject.command("find")
|
|
20
|
+
@click.argument("search_term")
|
|
21
|
+
@click.option(
|
|
22
|
+
"--method", "-m",
|
|
23
|
+
type=click.Choice(["by_name", "by_tag", "by_layer",
|
|
24
|
+
"by_component", "by_path", "by_id"]),
|
|
25
|
+
default="by_name",
|
|
26
|
+
help="Search method."
|
|
27
|
+
)
|
|
28
|
+
@click.option(
|
|
29
|
+
"--include-inactive", "-i",
|
|
30
|
+
is_flag=True,
|
|
31
|
+
help="Include inactive GameObjects."
|
|
32
|
+
)
|
|
33
|
+
@click.option(
|
|
34
|
+
"--limit", "-l",
|
|
35
|
+
default=50,
|
|
36
|
+
type=int,
|
|
37
|
+
help="Maximum results to return."
|
|
38
|
+
)
|
|
39
|
+
@click.option(
|
|
40
|
+
"--cursor", "-c",
|
|
41
|
+
default=0,
|
|
42
|
+
type=int,
|
|
43
|
+
help="Pagination cursor (offset)."
|
|
44
|
+
)
|
|
45
|
+
def find(search_term: str, method: str, include_inactive: bool, limit: int, cursor: int):
|
|
46
|
+
"""Find GameObjects by search criteria.
|
|
47
|
+
|
|
48
|
+
\b
|
|
49
|
+
Examples:
|
|
50
|
+
unity-mcp gameobject find "Player"
|
|
51
|
+
unity-mcp gameobject find "Enemy" --method by_tag
|
|
52
|
+
unity-mcp gameobject find "-81840" --method by_id
|
|
53
|
+
unity-mcp gameobject find "Rigidbody" --method by_component
|
|
54
|
+
unity-mcp gameobject find "/Canvas/Panel" --method by_path
|
|
55
|
+
"""
|
|
56
|
+
config = get_config()
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
result = run_command("find_gameobjects", {
|
|
60
|
+
"searchMethod": method,
|
|
61
|
+
"searchTerm": search_term,
|
|
62
|
+
"includeInactive": include_inactive,
|
|
63
|
+
"pageSize": limit,
|
|
64
|
+
"cursor": cursor,
|
|
65
|
+
}, config)
|
|
66
|
+
click.echo(format_output(result, config.format))
|
|
67
|
+
except UnityConnectionError as e:
|
|
68
|
+
print_error(str(e))
|
|
69
|
+
sys.exit(1)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@gameobject.command("create")
|
|
73
|
+
@click.argument("name")
|
|
74
|
+
@click.option(
|
|
75
|
+
"--primitive", "-p",
|
|
76
|
+
type=click.Choice(["Cube", "Sphere", "Cylinder",
|
|
77
|
+
"Plane", "Capsule", "Quad"]),
|
|
78
|
+
help="Create a primitive type."
|
|
79
|
+
)
|
|
80
|
+
@click.option(
|
|
81
|
+
"--position", "-pos",
|
|
82
|
+
nargs=3,
|
|
83
|
+
type=float,
|
|
84
|
+
default=None,
|
|
85
|
+
help="Position as X Y Z."
|
|
86
|
+
)
|
|
87
|
+
@click.option(
|
|
88
|
+
"--rotation", "-rot",
|
|
89
|
+
nargs=3,
|
|
90
|
+
type=float,
|
|
91
|
+
default=None,
|
|
92
|
+
help="Rotation as X Y Z (euler angles)."
|
|
93
|
+
)
|
|
94
|
+
@click.option(
|
|
95
|
+
"--scale", "-s",
|
|
96
|
+
nargs=3,
|
|
97
|
+
type=float,
|
|
98
|
+
default=None,
|
|
99
|
+
help="Scale as X Y Z."
|
|
100
|
+
)
|
|
101
|
+
@click.option(
|
|
102
|
+
"--parent",
|
|
103
|
+
default=None,
|
|
104
|
+
help="Parent GameObject name or path."
|
|
105
|
+
)
|
|
106
|
+
@click.option(
|
|
107
|
+
"--tag", "-t",
|
|
108
|
+
default=None,
|
|
109
|
+
help="Tag to assign."
|
|
110
|
+
)
|
|
111
|
+
@click.option(
|
|
112
|
+
"--layer",
|
|
113
|
+
default=None,
|
|
114
|
+
help="Layer to assign."
|
|
115
|
+
)
|
|
116
|
+
@click.option(
|
|
117
|
+
"--components",
|
|
118
|
+
default=None,
|
|
119
|
+
help="Comma-separated list of components to add."
|
|
120
|
+
)
|
|
121
|
+
@click.option(
|
|
122
|
+
"--save-prefab",
|
|
123
|
+
is_flag=True,
|
|
124
|
+
help="Save as prefab after creation."
|
|
125
|
+
)
|
|
126
|
+
@click.option(
|
|
127
|
+
"--prefab-path",
|
|
128
|
+
default=None,
|
|
129
|
+
help="Path for prefab (e.g., Assets/Prefabs/MyPrefab.prefab)."
|
|
130
|
+
)
|
|
131
|
+
def create(
|
|
132
|
+
name: str,
|
|
133
|
+
primitive: Optional[str],
|
|
134
|
+
position: Optional[Tuple[float, float, float]],
|
|
135
|
+
rotation: Optional[Tuple[float, float, float]],
|
|
136
|
+
scale: Optional[Tuple[float, float, float]],
|
|
137
|
+
parent: Optional[str],
|
|
138
|
+
tag: Optional[str],
|
|
139
|
+
layer: Optional[str],
|
|
140
|
+
components: Optional[str],
|
|
141
|
+
save_prefab: bool,
|
|
142
|
+
prefab_path: Optional[str],
|
|
143
|
+
):
|
|
144
|
+
"""Create a new GameObject.
|
|
145
|
+
|
|
146
|
+
\b
|
|
147
|
+
Examples:
|
|
148
|
+
unity-mcp gameobject create "MyCube" --primitive Cube
|
|
149
|
+
unity-mcp gameobject create "Player" --position 0 1 0
|
|
150
|
+
unity-mcp gameobject create "Enemy" --primitive Sphere --tag Enemy
|
|
151
|
+
unity-mcp gameobject create "Child" --parent "ParentObject"
|
|
152
|
+
unity-mcp gameobject create "Item" --components "Rigidbody,BoxCollider"
|
|
153
|
+
"""
|
|
154
|
+
config = get_config()
|
|
155
|
+
|
|
156
|
+
params: dict[str, Any] = {
|
|
157
|
+
"action": "create",
|
|
158
|
+
"name": name,
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if primitive:
|
|
162
|
+
params["primitiveType"] = primitive
|
|
163
|
+
if position:
|
|
164
|
+
params["position"] = list(position)
|
|
165
|
+
if rotation:
|
|
166
|
+
params["rotation"] = list(rotation)
|
|
167
|
+
if scale:
|
|
168
|
+
params["scale"] = list(scale)
|
|
169
|
+
if parent:
|
|
170
|
+
params["parent"] = parent
|
|
171
|
+
if tag:
|
|
172
|
+
params["tag"] = tag
|
|
173
|
+
if layer:
|
|
174
|
+
params["layer"] = layer
|
|
175
|
+
if save_prefab:
|
|
176
|
+
params["saveAsPrefab"] = True
|
|
177
|
+
if prefab_path:
|
|
178
|
+
params["prefabPath"] = prefab_path
|
|
179
|
+
|
|
180
|
+
try:
|
|
181
|
+
result = run_command("manage_gameobject", params, config)
|
|
182
|
+
|
|
183
|
+
# Add components separately since componentsToAdd doesn't work
|
|
184
|
+
if components and (result.get("success") or result.get("data") or result.get("result")):
|
|
185
|
+
component_list = [c.strip() for c in components.split(",")]
|
|
186
|
+
failed_components = []
|
|
187
|
+
for component in component_list:
|
|
188
|
+
try:
|
|
189
|
+
run_command("manage_components", {
|
|
190
|
+
"action": "add",
|
|
191
|
+
"target": name,
|
|
192
|
+
"componentType": component,
|
|
193
|
+
}, config)
|
|
194
|
+
except UnityConnectionError:
|
|
195
|
+
failed_components.append(component)
|
|
196
|
+
if failed_components:
|
|
197
|
+
print_warning(
|
|
198
|
+
f"Failed to add components: {', '.join(failed_components)}")
|
|
199
|
+
|
|
200
|
+
click.echo(format_output(result, config.format))
|
|
201
|
+
if result.get("success") or result.get("result"):
|
|
202
|
+
print_success(f"Created GameObject '{name}'")
|
|
203
|
+
except UnityConnectionError as e:
|
|
204
|
+
print_error(str(e))
|
|
205
|
+
sys.exit(1)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@gameobject.command("modify")
|
|
209
|
+
@click.argument("target")
|
|
210
|
+
@click.option(
|
|
211
|
+
"--name", "-n",
|
|
212
|
+
default=None,
|
|
213
|
+
help="New name for the GameObject."
|
|
214
|
+
)
|
|
215
|
+
@click.option(
|
|
216
|
+
"--position", "-pos",
|
|
217
|
+
nargs=3,
|
|
218
|
+
type=float,
|
|
219
|
+
default=None,
|
|
220
|
+
help="New position as X Y Z."
|
|
221
|
+
)
|
|
222
|
+
@click.option(
|
|
223
|
+
"--rotation", "-rot",
|
|
224
|
+
nargs=3,
|
|
225
|
+
type=float,
|
|
226
|
+
default=None,
|
|
227
|
+
help="New rotation as X Y Z (euler angles)."
|
|
228
|
+
)
|
|
229
|
+
@click.option(
|
|
230
|
+
"--scale", "-s",
|
|
231
|
+
nargs=3,
|
|
232
|
+
type=float,
|
|
233
|
+
default=None,
|
|
234
|
+
help="New scale as X Y Z."
|
|
235
|
+
)
|
|
236
|
+
@click.option(
|
|
237
|
+
"--parent",
|
|
238
|
+
default=None,
|
|
239
|
+
help="New parent GameObject."
|
|
240
|
+
)
|
|
241
|
+
@click.option(
|
|
242
|
+
"--tag", "-t",
|
|
243
|
+
default=None,
|
|
244
|
+
help="New tag."
|
|
245
|
+
)
|
|
246
|
+
@click.option(
|
|
247
|
+
"--layer",
|
|
248
|
+
default=None,
|
|
249
|
+
help="New layer."
|
|
250
|
+
)
|
|
251
|
+
@click.option(
|
|
252
|
+
"--active/--inactive",
|
|
253
|
+
default=None,
|
|
254
|
+
help="Set active state."
|
|
255
|
+
)
|
|
256
|
+
@click.option(
|
|
257
|
+
"--add-components",
|
|
258
|
+
default=None,
|
|
259
|
+
help="Comma-separated list of components to add."
|
|
260
|
+
)
|
|
261
|
+
@click.option(
|
|
262
|
+
"--remove-components",
|
|
263
|
+
default=None,
|
|
264
|
+
help="Comma-separated list of components to remove."
|
|
265
|
+
)
|
|
266
|
+
@click.option(
|
|
267
|
+
"--search-method",
|
|
268
|
+
type=click.Choice(["by_name", "by_path", "by_tag", "by_id"]),
|
|
269
|
+
default=None,
|
|
270
|
+
help="How to find the target GameObject."
|
|
271
|
+
)
|
|
272
|
+
def modify(
|
|
273
|
+
target: str,
|
|
274
|
+
name: Optional[str],
|
|
275
|
+
position: Optional[Tuple[float, float, float]],
|
|
276
|
+
rotation: Optional[Tuple[float, float, float]],
|
|
277
|
+
scale: Optional[Tuple[float, float, float]],
|
|
278
|
+
parent: Optional[str],
|
|
279
|
+
tag: Optional[str],
|
|
280
|
+
layer: Optional[str],
|
|
281
|
+
active: Optional[bool],
|
|
282
|
+
add_components: Optional[str],
|
|
283
|
+
remove_components: Optional[str],
|
|
284
|
+
search_method: Optional[str],
|
|
285
|
+
):
|
|
286
|
+
"""Modify an existing GameObject.
|
|
287
|
+
|
|
288
|
+
TARGET can be a name, path, instance ID, or tag depending on --search-method.
|
|
289
|
+
|
|
290
|
+
\b
|
|
291
|
+
Examples:
|
|
292
|
+
unity-mcp gameobject modify "Player" --position 0 5 0
|
|
293
|
+
unity-mcp gameobject modify "Enemy" --name "Boss" --tag "Boss"
|
|
294
|
+
unity-mcp gameobject modify "-81840" --search-method by_id --active
|
|
295
|
+
unity-mcp gameobject modify "/Canvas/Panel" --search-method by_path --inactive
|
|
296
|
+
unity-mcp gameobject modify "Cube" --add-components "Rigidbody,BoxCollider"
|
|
297
|
+
"""
|
|
298
|
+
config = get_config()
|
|
299
|
+
|
|
300
|
+
params: dict[str, Any] = {
|
|
301
|
+
"action": "modify",
|
|
302
|
+
"target": target,
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if name:
|
|
306
|
+
params["name"] = name
|
|
307
|
+
if position:
|
|
308
|
+
params["position"] = list(position)
|
|
309
|
+
if rotation:
|
|
310
|
+
params["rotation"] = list(rotation)
|
|
311
|
+
if scale:
|
|
312
|
+
params["scale"] = list(scale)
|
|
313
|
+
if parent:
|
|
314
|
+
params["parent"] = parent
|
|
315
|
+
if tag:
|
|
316
|
+
params["tag"] = tag
|
|
317
|
+
if layer:
|
|
318
|
+
params["layer"] = layer
|
|
319
|
+
if active is not None:
|
|
320
|
+
params["setActive"] = active
|
|
321
|
+
if add_components:
|
|
322
|
+
params["componentsToAdd"] = [c.strip()
|
|
323
|
+
for c in add_components.split(",")]
|
|
324
|
+
if remove_components:
|
|
325
|
+
params["componentsToRemove"] = [c.strip()
|
|
326
|
+
for c in remove_components.split(",")]
|
|
327
|
+
if search_method:
|
|
328
|
+
params["searchMethod"] = search_method
|
|
329
|
+
|
|
330
|
+
try:
|
|
331
|
+
result = run_command("manage_gameobject", params, config)
|
|
332
|
+
click.echo(format_output(result, config.format))
|
|
333
|
+
except UnityConnectionError as e:
|
|
334
|
+
print_error(str(e))
|
|
335
|
+
sys.exit(1)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
@gameobject.command("delete")
|
|
339
|
+
@click.argument("target")
|
|
340
|
+
@click.option(
|
|
341
|
+
"--search-method",
|
|
342
|
+
type=click.Choice(["by_name", "by_path", "by_tag", "by_id"]),
|
|
343
|
+
default=None,
|
|
344
|
+
help="How to find the target GameObject."
|
|
345
|
+
)
|
|
346
|
+
@click.option(
|
|
347
|
+
"--force", "-f",
|
|
348
|
+
is_flag=True,
|
|
349
|
+
help="Skip confirmation prompt."
|
|
350
|
+
)
|
|
351
|
+
def delete(target: str, search_method: Optional[str], force: bool):
|
|
352
|
+
"""Delete a GameObject.
|
|
353
|
+
|
|
354
|
+
\b
|
|
355
|
+
Examples:
|
|
356
|
+
unity-mcp gameobject delete "OldObject"
|
|
357
|
+
unity-mcp gameobject delete "-81840" --search-method by_id
|
|
358
|
+
unity-mcp gameobject delete "TempObjects" --search-method by_tag --force
|
|
359
|
+
"""
|
|
360
|
+
config = get_config()
|
|
361
|
+
|
|
362
|
+
if not force:
|
|
363
|
+
click.confirm(f"Delete GameObject '{target}'?", abort=True)
|
|
364
|
+
|
|
365
|
+
params = {
|
|
366
|
+
"action": "delete",
|
|
367
|
+
"target": target,
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if search_method:
|
|
371
|
+
params["searchMethod"] = search_method
|
|
372
|
+
|
|
373
|
+
try:
|
|
374
|
+
result = run_command("manage_gameobject", params, config)
|
|
375
|
+
click.echo(format_output(result, config.format))
|
|
376
|
+
if result.get("success"):
|
|
377
|
+
print_success(f"Deleted GameObject '{target}'")
|
|
378
|
+
except UnityConnectionError as e:
|
|
379
|
+
print_error(str(e))
|
|
380
|
+
sys.exit(1)
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
@gameobject.command("duplicate")
|
|
384
|
+
@click.argument("target")
|
|
385
|
+
@click.option(
|
|
386
|
+
"--name", "-n",
|
|
387
|
+
default=None,
|
|
388
|
+
help="Name for the duplicate (default: OriginalName_Copy)."
|
|
389
|
+
)
|
|
390
|
+
@click.option(
|
|
391
|
+
"--offset",
|
|
392
|
+
nargs=3,
|
|
393
|
+
type=float,
|
|
394
|
+
default=None,
|
|
395
|
+
help="Position offset from original as X Y Z."
|
|
396
|
+
)
|
|
397
|
+
@click.option(
|
|
398
|
+
"--search-method",
|
|
399
|
+
type=click.Choice(["by_name", "by_path", "by_tag", "by_id"]),
|
|
400
|
+
default=None,
|
|
401
|
+
help="How to find the target GameObject."
|
|
402
|
+
)
|
|
403
|
+
def duplicate(
|
|
404
|
+
target: str,
|
|
405
|
+
name: Optional[str],
|
|
406
|
+
offset: Optional[Tuple[float, float, float]],
|
|
407
|
+
search_method: Optional[str],
|
|
408
|
+
):
|
|
409
|
+
"""Duplicate a GameObject.
|
|
410
|
+
|
|
411
|
+
\b
|
|
412
|
+
Examples:
|
|
413
|
+
unity-mcp gameobject duplicate "Player"
|
|
414
|
+
unity-mcp gameobject duplicate "Enemy" --name "Enemy2" --offset 5 0 0
|
|
415
|
+
unity-mcp gameobject duplicate "-81840" --search-method by_id
|
|
416
|
+
"""
|
|
417
|
+
config = get_config()
|
|
418
|
+
|
|
419
|
+
params: dict[str, Any] = {
|
|
420
|
+
"action": "duplicate",
|
|
421
|
+
"target": target,
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if name:
|
|
425
|
+
params["new_name"] = name
|
|
426
|
+
if offset:
|
|
427
|
+
params["offset"] = list(offset)
|
|
428
|
+
if search_method:
|
|
429
|
+
params["searchMethod"] = search_method
|
|
430
|
+
|
|
431
|
+
try:
|
|
432
|
+
result = run_command("manage_gameobject", params, config)
|
|
433
|
+
click.echo(format_output(result, config.format))
|
|
434
|
+
if result.get("success"):
|
|
435
|
+
print_success(f"Duplicated GameObject '{target}'")
|
|
436
|
+
except UnityConnectionError as e:
|
|
437
|
+
print_error(str(e))
|
|
438
|
+
sys.exit(1)
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
@gameobject.command("move")
|
|
442
|
+
@click.argument("target")
|
|
443
|
+
@click.option(
|
|
444
|
+
"--reference", "-r",
|
|
445
|
+
required=True,
|
|
446
|
+
help="Reference object for relative movement."
|
|
447
|
+
)
|
|
448
|
+
@click.option(
|
|
449
|
+
"--direction", "-d",
|
|
450
|
+
type=click.Choice(["left", "right", "up", "down", "forward",
|
|
451
|
+
"back", "front", "backward", "behind"]),
|
|
452
|
+
required=True,
|
|
453
|
+
help="Direction to move."
|
|
454
|
+
)
|
|
455
|
+
@click.option(
|
|
456
|
+
"--distance",
|
|
457
|
+
type=float,
|
|
458
|
+
default=1.0,
|
|
459
|
+
help="Distance to move (default: 1.0)."
|
|
460
|
+
)
|
|
461
|
+
@click.option(
|
|
462
|
+
"--local",
|
|
463
|
+
is_flag=True,
|
|
464
|
+
help="Use reference object's local space instead of world space."
|
|
465
|
+
)
|
|
466
|
+
@click.option(
|
|
467
|
+
"--search-method",
|
|
468
|
+
type=click.Choice(["by_name", "by_path", "by_tag", "by_id"]),
|
|
469
|
+
default=None,
|
|
470
|
+
help="How to find the target GameObject."
|
|
471
|
+
)
|
|
472
|
+
def move(
|
|
473
|
+
target: str,
|
|
474
|
+
reference: str,
|
|
475
|
+
direction: str,
|
|
476
|
+
distance: float,
|
|
477
|
+
local: bool,
|
|
478
|
+
search_method: Optional[str],
|
|
479
|
+
):
|
|
480
|
+
"""Move a GameObject relative to another object.
|
|
481
|
+
|
|
482
|
+
\b
|
|
483
|
+
Examples:
|
|
484
|
+
unity-mcp gameobject move "Chair" --reference "Table" --direction right --distance 2
|
|
485
|
+
unity-mcp gameobject move "Light" --reference "Player" --direction up --distance 3
|
|
486
|
+
unity-mcp gameobject move "NPC" --reference "Player" --direction forward --local
|
|
487
|
+
"""
|
|
488
|
+
config = get_config()
|
|
489
|
+
|
|
490
|
+
params: dict[str, Any] = {
|
|
491
|
+
"action": "move_relative",
|
|
492
|
+
"target": target,
|
|
493
|
+
"reference_object": reference,
|
|
494
|
+
"direction": direction,
|
|
495
|
+
"distance": distance,
|
|
496
|
+
"world_space": not local,
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if search_method:
|
|
500
|
+
params["searchMethod"] = search_method
|
|
501
|
+
|
|
502
|
+
try:
|
|
503
|
+
result = run_command("manage_gameobject", params, config)
|
|
504
|
+
click.echo(format_output(result, config.format))
|
|
505
|
+
if result.get("success"):
|
|
506
|
+
print_success(
|
|
507
|
+
f"Moved '{target}' {direction} of '{reference}' by {distance} units")
|
|
508
|
+
except UnityConnectionError as e:
|
|
509
|
+
print_error(str(e))
|
|
510
|
+
sys.exit(1)
|
cli/commands/instance.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Instance CLI commands for managing Unity instances."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import click
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from cli.utils.config import get_config
|
|
8
|
+
from cli.utils.output import format_output, print_error, print_success, print_info
|
|
9
|
+
from cli.utils.connection import run_command, run_list_instances, UnityConnectionError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.group()
|
|
13
|
+
def instance():
|
|
14
|
+
"""Unity instance management - list, select, and view instances."""
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@instance.command("list")
|
|
19
|
+
def list_instances():
|
|
20
|
+
"""List available Unity instances.
|
|
21
|
+
|
|
22
|
+
\\b
|
|
23
|
+
Examples:
|
|
24
|
+
unity-mcp instance list
|
|
25
|
+
"""
|
|
26
|
+
config = get_config()
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
result = run_list_instances(config)
|
|
30
|
+
instances = result.get("instances", []) if isinstance(
|
|
31
|
+
result, dict) else []
|
|
32
|
+
|
|
33
|
+
if not instances:
|
|
34
|
+
print_info("No Unity instances currently connected")
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
click.echo("Available Unity instances:")
|
|
38
|
+
for inst in instances:
|
|
39
|
+
project = inst.get("project", "Unknown")
|
|
40
|
+
version = inst.get("unity_version", "Unknown")
|
|
41
|
+
hash_id = inst.get("hash", "")
|
|
42
|
+
session_id = inst.get("session_id", "")
|
|
43
|
+
|
|
44
|
+
# Format: ProjectName@hash (Unity version)
|
|
45
|
+
display_id = f"{project}@{hash_id}" if hash_id else project
|
|
46
|
+
click.echo(f" • {display_id} (Unity {version})")
|
|
47
|
+
if session_id:
|
|
48
|
+
click.echo(f" Session: {session_id[:8]}...")
|
|
49
|
+
|
|
50
|
+
except UnityConnectionError as e:
|
|
51
|
+
print_error(str(e))
|
|
52
|
+
sys.exit(1)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@instance.command("set")
|
|
56
|
+
@click.argument("instance_id")
|
|
57
|
+
def set_instance(instance_id: str):
|
|
58
|
+
"""Set the active Unity instance.
|
|
59
|
+
|
|
60
|
+
INSTANCE_ID can be Name@hash or just a hash prefix.
|
|
61
|
+
|
|
62
|
+
\\b
|
|
63
|
+
Examples:
|
|
64
|
+
unity-mcp instance set "MyProject@abc123"
|
|
65
|
+
unity-mcp instance set abc123
|
|
66
|
+
"""
|
|
67
|
+
config = get_config()
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
result = run_command("set_active_instance", {
|
|
71
|
+
"instance": instance_id,
|
|
72
|
+
}, config)
|
|
73
|
+
click.echo(format_output(result, config.format))
|
|
74
|
+
if result.get("success"):
|
|
75
|
+
data = result.get("data", {})
|
|
76
|
+
active = data.get("instance", instance_id)
|
|
77
|
+
print_success(f"Active instance set to: {active}")
|
|
78
|
+
except UnityConnectionError as e:
|
|
79
|
+
print_error(str(e))
|
|
80
|
+
sys.exit(1)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@instance.command("current")
|
|
84
|
+
def current_instance():
|
|
85
|
+
"""Show the currently selected Unity instance.
|
|
86
|
+
|
|
87
|
+
\\b
|
|
88
|
+
Examples:
|
|
89
|
+
unity-mcp instance current
|
|
90
|
+
"""
|
|
91
|
+
config = get_config()
|
|
92
|
+
|
|
93
|
+
# The current instance is typically shown in telemetry or needs to be tracked
|
|
94
|
+
# For now, we can show the configured instance from CLI options
|
|
95
|
+
if config.unity_instance:
|
|
96
|
+
click.echo(f"Configured instance: {config.unity_instance}")
|
|
97
|
+
else:
|
|
98
|
+
print_info(
|
|
99
|
+
"No instance explicitly set. Using default (auto-select single instance).")
|
|
100
|
+
print_info("Use 'unity-mcp instance list' to see available instances.")
|
|
101
|
+
print_info("Use 'unity-mcp instance set <id>' to select one.")
|