Skip to content

Commit feddc72

Browse files
committed
Update based on feedback
1 parent c718aaa commit feddc72

12 files changed

Lines changed: 206 additions & 39 deletions

File tree

MCPForUnity/Editor/Tools/Physics/JointOps.cs

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,21 @@ public static object AddJoint(JObject @params)
5656
bool is2D;
5757

5858
if (dimensionParam == "2d")
59-
is2D = has2DRb;
59+
{
60+
if (!has2DRb)
61+
return new ErrorResponse($"Target '{go.name}' has no Rigidbody2D.");
62+
is2D = true;
63+
}
6064
else if (dimensionParam == "3d")
65+
{
66+
if (!is3D)
67+
return new ErrorResponse($"Target '{go.name}' has no Rigidbody.");
6168
is2D = false;
69+
}
70+
else if (!string.IsNullOrEmpty(dimensionParam))
71+
{
72+
return new ErrorResponse($"Invalid dimension: '{dimensionParam}'. Use '3d' or '2d'.");
73+
}
6274
else
6375
{
6476
// Auto-detect; if both, prefer 3D (3D is the default physics)
@@ -78,34 +90,40 @@ public static object AddJoint(JObject @params)
7890
return new ErrorResponse($"Unknown {dimension} joint type: '{jointTypeStr}'. Valid types: {validTypes}.");
7991
}
8092

81-
var joint = Undo.AddComponent(go, jointComponentType);
82-
if (joint == null)
83-
return new ErrorResponse($"Failed to add {jointComponentType.Name} to '{go.name}'.");
84-
85-
// Set connected body if specified
93+
// Validate connected body before mutating the scene
8694
string connectedBodyTarget = p.Get("connected_body");
95+
GameObject connectedGo = null;
8796
if (!string.IsNullOrEmpty(connectedBodyTarget))
8897
{
89-
GameObject connectedGo = FindTarget(new JValue(connectedBodyTarget), searchMethod);
98+
connectedGo = FindTarget(new JValue(connectedBodyTarget), searchMethod);
9099
if (connectedGo == null)
91100
return new ErrorResponse($"Connected body GameObject '{connectedBodyTarget}' not found.");
92101

93102
if (is2D)
94103
{
95-
var rb2d = connectedGo.GetComponent<Rigidbody2D>();
96-
if (rb2d == null)
104+
if (connectedGo.GetComponent<Rigidbody2D>() == null)
97105
return new ErrorResponse($"Connected body '{connectedGo.name}' has no Rigidbody2D.");
98-
((Joint2D)joint).connectedBody = rb2d;
99106
}
100107
else
101108
{
102-
var rb = connectedGo.GetComponent<Rigidbody>();
103-
if (rb == null)
109+
if (connectedGo.GetComponent<Rigidbody>() == null)
104110
return new ErrorResponse($"Connected body '{connectedGo.name}' has no Rigidbody.");
105-
((Joint)joint).connectedBody = rb;
106111
}
107112
}
108113

114+
var joint = Undo.AddComponent(go, jointComponentType);
115+
if (joint == null)
116+
return new ErrorResponse($"Failed to add {jointComponentType.Name} to '{go.name}'.");
117+
118+
// Set connected body now that the joint is created
119+
if (connectedGo != null)
120+
{
121+
if (is2D)
122+
((Joint2D)joint).connectedBody = connectedGo.GetComponent<Rigidbody2D>();
123+
else
124+
((Joint)joint).connectedBody = connectedGo.GetComponent<Rigidbody>();
125+
}
126+
109127
// Set properties via reflection if provided
110128
var properties = p.GetRaw("properties") as JObject;
111129
if (properties != null)

MCPForUnity/Editor/Tools/Physics/ManagePhysics.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ public static object HandleCommand(JObject @params)
9898
catch (Exception ex)
9999
{
100100
McpLog.Error($"[ManagePhysics] Action '{action}' failed: {ex}");
101-
return new ErrorResponse($"Error in action '{action}': {ex.Message}");
101+
return new ErrorResponse(
102+
$"Error in action '{action}': {ex.Message}",
103+
new { stackTrace = ex.StackTrace }
104+
);
102105
}
103106
}
104107
}

MCPForUnity/Editor/Tools/Physics/PhysicsMaterialOps.cs

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ private static object Create3D(string name, string folder, ToolParams p)
158158

159159
string assetPath = $"{folder}/{name}.physicMaterial";
160160

161+
if (AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(assetPath) != null)
162+
return new ErrorResponse($"A physics material already exists at '{assetPath}'. Use configure_physics_material to modify it.");
163+
161164
#if UNITY_6000_0_OR_NEWER
162165
var mat = new PhysicsMaterial(name)
163166
{
@@ -217,6 +220,9 @@ private static object Create2D(string name, string folder, ToolParams p)
217220

218221
string assetPath = $"{folder}/{name}.physicsMaterial2D";
219222

223+
if (AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(assetPath) != null)
224+
return new ErrorResponse($"A 2D physics material already exists at '{assetPath}'. Use configure_physics_material to modify it.");
225+
220226
var mat = new PhysicsMaterial2D(name)
221227
{
222228
friction = friction,
@@ -294,34 +300,30 @@ private static object Configure3D(string path, JObject properties)
294300
case "frictioncombine":
295301
{
296302
#if UNITY_6000_0_OR_NEWER
297-
if (Enum.TryParse<PhysicsMaterialCombine>(prop.Value.ToString(), true, out var fc))
298-
{
299-
mat.frictionCombine = fc;
300-
changed.Add("frictionCombine");
301-
}
303+
if (!Enum.TryParse<PhysicsMaterialCombine>(prop.Value.ToString(), true, out var fc))
304+
return new ErrorResponse($"Invalid friction_combine value: '{prop.Value}'. Valid values: Average, Minimum, Maximum, Multiply.");
305+
mat.frictionCombine = fc;
306+
changed.Add("frictionCombine");
302307
#else
303-
if (Enum.TryParse<PhysicMaterialCombine>(prop.Value.ToString(), true, out var fc))
304-
{
305-
mat.frictionCombine = fc;
306-
changed.Add("frictionCombine");
307-
}
308+
if (!Enum.TryParse<PhysicMaterialCombine>(prop.Value.ToString(), true, out var fc))
309+
return new ErrorResponse($"Invalid friction_combine value: '{prop.Value}'. Valid values: Average, Minimum, Maximum, Multiply.");
310+
mat.frictionCombine = fc;
311+
changed.Add("frictionCombine");
308312
#endif
309313
break;
310314
}
311315
case "bouncecombine":
312316
{
313317
#if UNITY_6000_0_OR_NEWER
314-
if (Enum.TryParse<PhysicsMaterialCombine>(prop.Value.ToString(), true, out var bc))
315-
{
316-
mat.bounceCombine = bc;
317-
changed.Add("bounceCombine");
318-
}
318+
if (!Enum.TryParse<PhysicsMaterialCombine>(prop.Value.ToString(), true, out var bc))
319+
return new ErrorResponse($"Invalid bounce_combine value: '{prop.Value}'. Valid values: Average, Minimum, Maximum, Multiply.");
320+
mat.bounceCombine = bc;
321+
changed.Add("bounceCombine");
319322
#else
320-
if (Enum.TryParse<PhysicMaterialCombine>(prop.Value.ToString(), true, out var bc))
321-
{
322-
mat.bounceCombine = bc;
323-
changed.Add("bounceCombine");
324-
}
323+
if (!Enum.TryParse<PhysicMaterialCombine>(prop.Value.ToString(), true, out var bc))
324+
return new ErrorResponse($"Invalid bounce_combine value: '{prop.Value}'. Valid values: Average, Minimum, Maximum, Multiply.");
325+
mat.bounceCombine = bc;
326+
changed.Add("bounceCombine");
325327
#endif
326328
break;
327329
}

MCPForUnity/Editor/Tools/Physics/PhysicsQueryOps.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections.Generic;
23
using Newtonsoft.Json.Linq;
34
using UnityEngine;
@@ -799,7 +800,7 @@ private static int ResolveLayerMask(string layerMaskStr)
799800
if (layer >= 0)
800801
return 1 << layer;
801802

802-
return ~0;
803+
throw new ArgumentException($"Unknown layer name: '{layerMaskStr}'. Use a valid layer name or integer mask.");
803804
}
804805
}
805806
}

MCPForUnity/Editor/Tools/Physics/PhysicsRigidbodyOps.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,11 @@ private static object GetRigidbody3D(GameObject go)
7979
["isKinematic"] = rb.isKinematic,
8080
["position"] = new[] { rb.position.x, rb.position.y, rb.position.z },
8181
["rotation"] = new[] { rb.rotation.x, rb.rotation.y, rb.rotation.z, rb.rotation.w },
82+
#if UNITY_6000_0_OR_NEWER
8283
["velocity"] = new[] { rb.linearVelocity.x, rb.linearVelocity.y, rb.linearVelocity.z },
84+
#else
85+
["velocity"] = new[] { rb.velocity.x, rb.velocity.y, rb.velocity.z },
86+
#endif
8387
["angularVelocity"] = new[] { rb.angularVelocity.x, rb.angularVelocity.y, rb.angularVelocity.z },
8488
["interpolation"] = rb.interpolation.ToString(),
8589
["collisionDetectionMode"] = rb.collisionDetectionMode.ToString(),
@@ -124,7 +128,11 @@ private static object GetRigidbody2D(GameObject go)
124128
["simulated"] = rb2d.simulated,
125129
["position"] = new[] { rb2d.position.x, rb2d.position.y },
126130
["rotation"] = rb2d.rotation,
131+
#if UNITY_6000_0_OR_NEWER
127132
["velocity"] = new[] { rb2d.linearVelocity.x, rb2d.linearVelocity.y },
133+
#else
134+
["velocity"] = new[] { rb2d.velocity.x, rb2d.velocity.y },
135+
#endif
128136
["angularVelocity"] = rb2d.angularVelocity,
129137
["collisionDetectionMode"] = rb2d.collisionDetectionMode.ToString(),
130138
["constraints"] = rb2d.constraints.ToString(),

MCPForUnity/Editor/Tools/Physics/PhysicsSimulationOps.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,11 @@ private static List<object> CollectTargetRigidbody(string targetStr, string sear
8787
name = go.name,
8888
instanceID = go.GetInstanceID(),
8989
position = new[] { rb2d.position.x, rb2d.position.y },
90+
#if UNITY_6000_0_OR_NEWER
9091
velocity = new[] { rb2d.linearVelocity.x, rb2d.linearVelocity.y },
92+
#else
93+
velocity = new[] { rb2d.velocity.x, rb2d.velocity.y },
94+
#endif
9195
angularVelocity = rb2d.angularVelocity
9296
});
9397
}
@@ -102,7 +106,11 @@ private static List<object> CollectTargetRigidbody(string targetStr, string sear
102106
name = go.name,
103107
instanceID = go.GetInstanceID(),
104108
position = new[] { rb.position.x, rb.position.y, rb.position.z },
109+
#if UNITY_6000_0_OR_NEWER
105110
velocity = new[] { rb.linearVelocity.x, rb.linearVelocity.y, rb.linearVelocity.z },
111+
#else
112+
velocity = new[] { rb.velocity.x, rb.velocity.y, rb.velocity.z },
113+
#endif
106114
angularVelocity = new[] { rb.angularVelocity.x, rb.angularVelocity.y, rb.angularVelocity.z }
107115
});
108116
}
@@ -118,7 +126,11 @@ private static List<object> CollectActiveRigidbodies(string dimension)
118126

119127
if (dimension == "2d")
120128
{
129+
#if UNITY_2022_2_OR_NEWER
121130
var allRb2d = Object.FindObjectsByType<Rigidbody2D>(FindObjectsSortMode.None);
131+
#else
132+
var allRb2d = Object.FindObjectsOfType<Rigidbody2D>();
133+
#endif
122134
foreach (var rb2d in allRb2d)
123135
{
124136
if (results.Count >= maxResults) break;
@@ -130,14 +142,22 @@ private static List<object> CollectActiveRigidbodies(string dimension)
130142
name = rb2d.gameObject.name,
131143
instanceID = rb2d.gameObject.GetInstanceID(),
132144
position = new[] { rb2d.position.x, rb2d.position.y },
145+
#if UNITY_6000_0_OR_NEWER
133146
velocity = new[] { rb2d.linearVelocity.x, rb2d.linearVelocity.y },
147+
#else
148+
velocity = new[] { rb2d.velocity.x, rb2d.velocity.y },
149+
#endif
134150
angularVelocity = rb2d.angularVelocity
135151
});
136152
}
137153
}
138154
else
139155
{
156+
#if UNITY_2022_2_OR_NEWER
140157
var allRb = Object.FindObjectsByType<Rigidbody>(FindObjectsSortMode.None);
158+
#else
159+
var allRb = Object.FindObjectsOfType<Rigidbody>();
160+
#endif
141161
foreach (var rb in allRb)
142162
{
143163
if (results.Count >= maxResults) break;
@@ -149,7 +169,11 @@ private static List<object> CollectActiveRigidbodies(string dimension)
149169
name = rb.gameObject.name,
150170
instanceID = rb.gameObject.GetInstanceID(),
151171
position = new[] { rb.position.x, rb.position.y, rb.position.z },
172+
#if UNITY_6000_0_OR_NEWER
152173
velocity = new[] { rb.linearVelocity.x, rb.linearVelocity.y, rb.linearVelocity.z },
174+
#else
175+
velocity = new[] { rb.velocity.x, rb.velocity.y, rb.velocity.z },
176+
#endif
153177
angularVelocity = new[] { rb.angularVelocity.x, rb.angularVelocity.y, rb.angularVelocity.z }
154178
});
155179
}

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<details>
2121
<summary><strong>Recent Updates</strong></summary>
2222

23-
* **v9.6.2 (beta)** — New `manage_physics` tool (20 actions): physics settings, layer collision matrix, physics materials, joints (5 3D + 9 2D types), queries (raycast, raycast_all, linecast, shapecast, overlap), force application (AddForce/AddTorque/AddExplosionForce), rigidbody configuration, scene-wide validation, and edit-mode simulation. Full 3D and 2D support.
23+
* **v9.6.2 (beta)** — New `manage_physics` tool (21 actions): physics settings, layer collision matrix, physics materials, joints (5 3D + 9 2D types), queries (raycast, raycast_all, linecast, shapecast, overlap), force application (AddForce/AddTorque/AddExplosionForce), rigidbody configuration, scene-wide validation, and edit-mode simulation. Full 3D and 2D support.
2424
* **v9.6.1** — QoL extensions: `manage_editor` gains undo/redo actions. `manage_scene` gains multi-scene editing (additive load, close, set active, move GO between scenes), scene templates (3d_basic, 2d_basic, etc.), and scene validation with auto-repair. New `manage_build` tool: trigger player builds, switch platforms, configure player settings, manage build scenes and profiles (Unity 6+), run batch builds across multiple platforms, and async job tracking with polling. New `MaxPollSeconds` infrastructure for long-running tool operations.
2525
* **v9.5.4** — New `unity_reflect` and `unity_docs` tools for API verification: inspect live C# APIs via reflection and fetch official Unity documentation (ScriptReference, Manual, package docs). New `manage_packages` tool: install, remove, search, and manage Unity packages and scoped registries. Includes input validation, dependency checks on removal, and git URL warnings.
2626
* **v9.5.3** — New `manage_graphics` tool (33 actions): volume/post-processing, light baking, rendering stats, pipeline settings, URP renderer features. 3 new resources: `volumes`, `rendering_stats`, `renderer_features`.

Server/src/cli/main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ def register_optional_command(module_name: str, command_name: str) -> None:
273273
("cli.commands.packages", "packages"),
274274
("cli.commands.reflect", "reflect"),
275275
("cli.commands.docs", "docs"),
276+
("cli.commands.physics", "physics"),
276277
]
277278

278279
for module_name, command_name in optional_commands:

docs/i18n/README-zh.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<details>
2121
<summary><strong>最近更新</strong></summary>
2222

23-
* **v9.6.2 (beta)** — 新增 `manage_physics` 工具(20个操作):物理设置、层碰撞矩阵、物理材质、关节(5种3D + 9种2D类型)、查询(raycast、raycast_all、linecast、shapecast、overlap)、力施加(AddForce/AddTorque/AddExplosionForce)、刚体配置、场景物理验证与编辑器模式模拟。全面支持3D和2D物理。
23+
* **v9.6.2 (beta)** — 新增 `manage_physics` 工具(21个操作):物理设置、层碰撞矩阵、物理材质、关节(5种3D + 9种2D类型)、查询(raycast、raycast_all、linecast、shapecast、overlap)、力施加(AddForce/AddTorque/AddExplosionForce)、刚体配置、场景物理验证与编辑器模式模拟。全面支持3D和2D物理。
2424
* **v9.6.1** — QoL 扩展:`manage_editor` 新增撤销/重做操作。`manage_scene` 新增多场景编辑(叠加加载、关闭、设置活动场景、跨场景移动物体)、场景模板(3d_basic、2d_basic 等)、场景验证与自动修复。新增 `manage_build` 工具:触发玩家构建、切换平台、配置玩家设置、管理构建场景和配置文件(Unity 6+)、跨多平台批量构建、异步任务跟踪与轮询。新增 `MaxPollSeconds` 基础设施,支持长时间运行的工具操作。
2525
* **v9.5.4** — 新增 `unity_reflect``unity_docs` 工具用于 API 验证:通过反射检查实时 C# API,获取官方 Unity 文档(ScriptReference、Manual、包文档)。新增 `manage_packages` 工具:安装、移除、搜索和管理 Unity 包及作用域注册表。包含输入验证、移除时依赖检查和 git URL 警告。
2626
* **v9.5.3** — 新增 `manage_graphics` 工具(33个操作):体积/后处理、光照烘焙、渲染统计、管线设置、URP渲染器特性。3个新资源:`volumes``rendering_stats``renderer_features`

0 commit comments

Comments
 (0)