Skip to content

Conversation

@Scriptwonder
Copy link
Collaborator

@Scriptwonder Scriptwonder commented Jan 7, 2026

Add ManageVFX to the current function, support:

  1. Modify LineRender, TrailRender properties
  2. Modify particle system (legacy) properties
  3. Modify VisualEffectGraph and use templates (does not support edit graph yet)
  4. Example:
截屏2026-01-06 下午11 50 24

Tested on Unity 6.2 with Mac. Want to inform the team on this @msanatan @dsarno before merging, since this is just a building ground for VFX-type features (I am only touching the basics of visualEffectGraph!)

PS: Will rebase my main after this:)

Summary by Sourcery

Introduce a unified Manage VFX toolchain spanning Unity editor and server, enabling scripted control and configuration of particle systems, VFX Graph, line renderers, and trail renderers via a single MCP tool.

New Features:

  • Add a comprehensive ManageVFX Unity editor tool that handles actions for ParticleSystem, VisualEffect (VFX Graph), LineRenderer, and TrailRenderer components, including querying, configuration, and playback control.
  • Expose a new server-side manage_vfx MCP tool that forwards high-level VFX management actions and parameters to the Unity ManageVFX editor tool.
  • Provide a float coercion utility on the server to robustly convert loosely-typed input values into floats for tool parameter handling.

Enhancements:

  • Document detailed usage patterns, targeting options, and action groups for VFX-related tools within the manage_vfx MCP interface.

Summary by CodeRabbit

  • New Features

    • Editor VFX management tool to create, configure and control Particle, VFX Graph, Line and Trail effects (playback, parameters, bursts, asset/template management, events).
    • Renderer and parsing helpers for materials, widths, colors, gradients and animation curves to simplify VFX property editing and extraction.
  • Chores

    • Improved float coercion for more robust parameter parsing.

✏️ Tip: You can customize this high-level summary in your review settings.

* Update the .Bat file to include runtime folder
* Fix the inconsistent EditorPrefs variable so the GUI change on Script Validation could cause real change.
String to Int for consistency
Allows users to generate/compile codes during Playmode
Upload the unity_claude_skill that can be uploaded to Claude for a combo of unity-mcp-skill.
1. Fix Original Roslyn Compilation Custom Tool to fit the V8 standard
2. Add a new panel in the GUI to see and toggle/untoggle the tools. The toggle feature will be implemented in the future, right now its implemented here to discuss with the team if this is a good feature to add;
3. Add few missing summary in certain tools
Merge and use the previous PR's solution to solve the windows issue.
Fix the layout problem of manage_script in the panel
To comply with the current server setting
Tested object generation/modification with batch and it works perfectly! We should push and let users test for a while and see

PS: I tried both VS Copilot and Claude Desktop. Claude Desktop works but VS Copilot does not due to the nested structure of batch. Will look into it more.
This reverts commit 55ee768, reversing
changes made to ae2eedd.
* 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.

* user can access the camera access through the tool menu, or through direct LLM calling. Both tested on Windows with Claude Desktop.
nitpicking changes
Similar to deploy.bat, but sideload it to MCP For Unity for easier deployment inside Unity menu.
Add ManageVFX to the current function, support:
1. Modify LineRender, TrailRender properties
2. Modify particle system (legacy) properties
3. Modify VisualEffectGraph and use templates (does not support edit graph yet)
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jan 7, 2026

Reviewer's Guide

Introduce a unified manage_vfx tool pair (Unity editor + server) to control ParticleSystem, VisualEffect Graph, LineRenderer, and TrailRenderer components via structured actions, plus a generic float coercion helper on the server for safer numeric parsing.

Sequence diagram for a manage_vfx particle_set_main call

sequenceDiagram
  actor caller
  participant manage_vfx_tool
  participant send_with_unity_instance
  participant async_send_command_with_retry
  participant unity_editor as Unity_Editor
  participant manage_vfx_unity as ManageVFX
  participant particle_system as ParticleSystem

  caller->>manage_vfx_tool: manage_vfx(action=particle_set_main, target, duration, start_speed,...)
  manage_vfx_tool->>manage_vfx_tool: validate action in ALL_ACTIONS
  manage_vfx_tool->>manage_vfx_tool: build params_dict
  manage_vfx_tool->>send_with_unity_instance: tool manage_vfx, params_dict
  send_with_unity_instance->>async_send_command_with_retry: manage_vfx, params_dict
  async_send_command_with_retry->>unity_editor: send command manage_vfx
  unity_editor->>manage_vfx_unity: HandleCommand(JObject params)
  manage_vfx_unity->>manage_vfx_unity: route to HandleParticleSystemAction(set_main)
  manage_vfx_unity->>particle_system: ParticleSetMain
  particle_system-->>manage_vfx_unity: updated main module
  manage_vfx_unity-->>async_send_command_with_retry: {success=true,...}
  async_send_command_with_retry-->>send_with_unity_instance: result
  send_with_unity_instance-->>manage_vfx_tool: result dict
  manage_vfx_tool-->>caller: result dict
Loading

Class diagram for Unity ManageVFX editor tool and related handlers

classDiagram
  class ManageVFX {
    <<static>>
    +HandleCommand(params JObject) object
    -ParseColor(token JToken) Color
    -ParseVector3(token JToken) Vector3
    -ParseVector4(token JToken) Vector4
    -ParseGradient(token JToken) Gradient
    -ParseAnimationCurve(token JToken, defaultValue float) AnimationCurve
    -FindTargetGameObject(params JObject) GameObject
  }

  class ParticleSystemHandlers {
    -HandleParticleSystemAction(params JObject, action string) object
    -FindParticleSystem(params JObject) ParticleSystem
    -ParseMinMaxCurve(token JToken, defaultValue float) MinMaxCurve
    -ParseMinMaxGradient(token JToken) MinMaxGradient
    -ParticleGetInfo(params JObject) object
    -ParticleSetMain(params JObject) object
    -ParticleSetEmission(params JObject) object
    -ParticleSetShape(params JObject) object
    -ParticleSetColorOverLifetime(params JObject) object
    -ParticleSetSizeOverLifetime(params JObject) object
    -ParticleSetVelocityOverLifetime(params JObject) object
    -ParticleSetNoise(params JObject) object
    -ParticleSetRenderer(params JObject) object
    -ParticleEnableModule(params JObject) object
    -ParticleControl(params JObject, action string) object
    -ParticleAddBurst(params JObject) object
    -ParticleClearBursts(params JObject) object
  }

  class VFXGraphHandlers {
    -HandleVFXGraphAction(params JObject, action string) object
    -FindVisualEffect(params JObject) VisualEffect
    -VFXCreateAsset(params JObject) object
    -VFXAssignAsset(params JObject) object
    -VFXListTemplates(params JObject) object
    -VFXListAssets(params JObject) object
    -VFXGetInfo(params JObject) object
    -VFXSetParameter(params JObject, setter Action) object
    -VFXSetVector(params JObject, dims int) object
    -VFXSetColor(params JObject) object
    -VFXSetGradient(params JObject) object
    -VFXSetTexture(params JObject) object
    -VFXSetMesh(params JObject) object
    -VFXSetCurve(params JObject) object
    -VFXSendEvent(params JObject) object
    -VFXControl(params JObject, action string) object
    -VFXSetPlaybackSpeed(params JObject) object
    -VFXSetSeed(params JObject) object
  }

  class LineRendererHandlers {
    -HandleLineRendererAction(params JObject, action string) object
    -FindLineRenderer(params JObject) LineRenderer
    -LineGetInfo(params JObject) object
    -LineSetPositions(params JObject) object
    -LineAddPosition(params JObject) object
    -LineSetPosition(params JObject) object
    -LineSetWidth(params JObject) object
    -LineSetColor(params JObject) object
    -LineSetMaterial(params JObject) object
    -LineSetProperties(params JObject) object
    -LineClear(params JObject) object
    -LineCreateLine(params JObject) object
    -LineCreateCircle(params JObject) object
    -LineCreateArc(params JObject) object
    -LineCreateBezier(params JObject) object
  }

  class TrailRendererHandlers {
    -HandleTrailRendererAction(params JObject, action string) object
    -FindTrailRenderer(params JObject) TrailRenderer
    -TrailGetInfo(params JObject) object
    -TrailSetTime(params JObject) object
    -TrailSetWidth(params JObject) object
    -TrailSetColor(params JObject) object
    -TrailSetMaterial(params JObject) object
    -TrailSetProperties(params JObject) object
    -TrailClear(params JObject) object
    -TrailEmit(params JObject) object
  }

  class manage_vfx_tool_py {
    <<tool function>>
    +manage_vfx(ctx Context, action str, ...) dict
  }

  class TransportLayer {
    +send_with_unity_instance(fn, unity_instance, tool_name str, params_dict dict) Any
    +async_send_command_with_retry(unity_instance, tool_name str, params_dict dict) Any
  }

  ManageVFX ..> ParticleSystemHandlers
  ManageVFX ..> VFXGraphHandlers
  ManageVFX ..> LineRendererHandlers
  ManageVFX ..> TrailRendererHandlers
  manage_vfx_tool_py --> TransportLayer
  TransportLayer --> ManageVFX
  ParticleSystemHandlers --> ParticleSystem
  VFXGraphHandlers --> VisualEffect
  LineRendererHandlers --> LineRenderer
  TrailRendererHandlers --> TrailRenderer
Loading

File-Level Changes

Change Details Files
Add a robust ManageVFX Unity editor tool that routes high-level actions to concrete operations on ParticleSystem, VisualEffect (VFX Graph), LineRenderer, and TrailRenderer components.
  • Implement a central HandleCommand dispatcher that routes actions by prefix (particle_, vfx_, line_, trail_) with a ping capability.
  • Add parsing helpers for colors, vectors, gradients, animation curves, and GameObject lookup, reused across component handlers.
  • Implement ParticleSystem handlers for querying info, configuring main/emission/shape and other modules, controlling playback, and managing bursts, including renderer configuration and module toggling.
  • Implement VFX Graph handlers for asset management (create, assign, list templates/assets) and runtime parameter/event control, guarded by UNITY_VFX_GRAPH defines.
  • Implement LineRenderer and TrailRenderer handlers to query state, mutate positions and width/color/material/settings, generate common shapes (line, circle, arc, Bezier), and clear/emit trails while preserving undo/dirty state.
MCPForUnity/Editor/Tools/ManageVFX.cs
Expose a server-side manage_vfx tool that validates actions, normalizes parameters, and forwards them to the Unity ManageVFX handler over the existing Unity transport.
  • Define allowed action sets grouped by component type (particle, vfx, line, trail) and validate the requested action name.
  • Document the tool comprehensively in the decorator description, clarifying targeting, component requirements, and per-prefix capabilities.
  • Define a large, typed parameter surface (using Annotated and Literals) mapping high-level arguments to the C# parameter names expected by ManageVFX, including renderer and shape options.
  • Construct a params_dict payload by conditionally adding non-None arguments and stripping Nones before sending via send_with_unity_instance/async_send_command_with_retry.
  • Return the raw Unity result when it is a dict, otherwise wrap it in a failure envelope.
Server/src/services/tools/manage_vfx.py
Add a generic float coercion helper on the server for tool inputs.
  • Implement coerce_float to convert loosely-typed values (ints, floats, numeric strings) into floats, ignoring booleans and treating empty/none-like strings as default.
  • Ensure invalid inputs fall back to the provided default instead of raising exceptions.
Server/src/services/tools/utils.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 7, 2026

Warning

Rate limit exceeded

@Scriptwonder has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 8 minutes and 43 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 2b12ac0 and 1fb83a1.

📒 Files selected for processing (2)
  • MCPForUnity/Editor/Helpers/VectorParsing.cs
  • MCPForUnity/Editor/Tools/ManageVFX.cs
📝 Walkthrough

Walkthrough

Adds a new Unity Editor tool (MCPForUnity.Editor.Tools.ManageVFX) with a central HandleCommand(JObject) API and matching server-side async manage_vfx service; supports legacy ParticleSystem, Visual Effect Graph, LineRenderer, and TrailRenderer operations, plus new parsing/renderer helpers and a coerce_float utility.

Changes

Cohort / File(s) Summary
Unity Editor tool
MCPForUnity/Editor/Tools/ManageVFX.cs, MCPForUnity/Editor/Tools/ManageVFX.cs.meta
New public static ManageVFX class with HandleCommand(JObject) entrypoint. Routes actions by prefixes (particle_*, vfx_*, line_*, trail_*) and implements many get_info/set_* handlers, VFX Graph asset/parameter APIs (guarded by UNITY_VFX_GRAPH), parsing/helpers, Undo/SetDirty, and target/material resolution.
Editor helpers
MCPForUnity/Editor/Helpers/RendererHelpers.cs, MCPForUnity/Editor/Helpers/RendererHelpers.cs.meta, MCPForUnity/Editor/Helpers/VectorParsing.cs
New RendererHelpers utilities for common renderer, width, color, material, and line/trail property handling; expanded VectorParsing with color, Vector4, Gradient, and AnimationCurve parsing helpers and defaulting variants.
Server-side tool
Server/src/services/tools/manage_vfx.py
New async manage_vfx function that validates actions (particle_/vfx_/line_/trail_), assembles nil-filtered params for Unity, dispatches via async_send_command_with_retry, and normalizes responses; includes detailed docstring and prefix-based suggestions on unknown actions.
Server utility
Server/src/services/tools/utils.py
Added coerce_float(value, default=None) to coerce inputs to float mirroring existing coerce_int behavior.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Server as manage_vfx.py
    participant Unity as ManageVFX.cs
    participant Component as VFX/Particle/Line/Trail

    Client->>Server: manage_vfx(action, target, params...)
    activate Server

    rect rgb(235,244,255)
    note over Server: Validate action & build params\n(filter out None)
    Server->>Server: Validate action vs ALL_ACTIONS
    Server->>Server: Populate params
    end

    Server->>Unity: send_command_with_retry → HandleCommand(params)
    activate Unity

    rect rgb(255,250,235)
    note over Unity: Route by action prefix\n(particle_*, vfx_*, line_*, trail_*)
    alt particle_*
        Unity->>Component: ParticleSystem ops (get/set, modules, bursts, play/stop)
    else vfx_*
        Unity->>Component: VisualEffect/VFX Graph ops (assets, params, events)
    else line_*
        Unity->>Component: LineRenderer ops (positions, width, color, create)
    else trail_*
        Unity->>Component: TrailRenderer ops (properties, emit, clear)
    end
    end

    Component-->>Unity: result/state
    Unity-->>Server: result
    deactivate Unity

    Server->>Server: Normalize response (dict)
    Server-->>Client: result
    deactivate Server
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰
I hop through shaders, dust, and light,
I stitch up trails in moonlit night.
One command — particles align,
Lines and VFX hum in time.
A rabbit cheers: render divine! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.74% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '[FEATURE]: Manage VFX function' clearly and specifically identifies the main addition—a new VFX management feature—aligning directly with the substantial new ManageVFX toolchain across both Unity editor and server.

✏️ Tip: You can configure your own custom Pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 4 issues, and left some high level feedback:

  • The manage_vfx tool function has an extremely large parameter list and a very long body; consider splitting it into smaller, component-specific helpers (e.g., particle, vfx, line, trail) to improve readability and maintainability and to reduce the chance of drift between the Python and C# parameter mappings.
  • In ManageVFX.VFXControl, the pause action toggles vfx.pause instead of setting it explicitly, which can be surprising if the caller expects idempotent behavior; consider using a separate resume action or an explicit isPaused parameter rather than toggling.
  • There is quite a bit of duplicated logic for applying common renderer properties across ParticleSystemRenderer, LineRenderer, and TrailRenderer; consider extracting shared helper methods (e.g., for shadows, probes, sorting) to reduce duplication and keep behavior consistent across renderer types.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `manage_vfx` tool function has an extremely large parameter list and a very long body; consider splitting it into smaller, component-specific helpers (e.g., particle, vfx, line, trail) to improve readability and maintainability and to reduce the chance of drift between the Python and C# parameter mappings.
- In `ManageVFX.VFXControl`, the `pause` action toggles `vfx.pause` instead of setting it explicitly, which can be surprising if the caller expects idempotent behavior; consider using a separate `resume` action or an explicit `isPaused` parameter rather than toggling.
- There is quite a bit of duplicated logic for applying common renderer properties across ParticleSystemRenderer, LineRenderer, and TrailRenderer; consider extracting shared helper methods (e.g., for shadows, probes, sorting) to reduce duplication and keep behavior consistent across renderer types.

## Individual Comments

### Comment 1
<location> `Server/src/services/tools/utils.py:80-94` </location>
<code_context>
         return default


+def coerce_float(value: Any, default: float | None = None) -> float | None:
+    """Attempt to coerce a loosely-typed value to a float."""
+    if value is None:
+        return default
+    try:
+        if isinstance(value, bool):
+            return default
+        if isinstance(value, (int, float)):
+            return float(value)
+        s = str(value).strip()
+        if s.lower() in ("", "none", "null"):
+            return default
+        return float(s)
+    except Exception:
+        return default
+      
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Narrow the exception type and reconsider special-casing bools in `coerce_float`.

Catching `Exception` here will also hide unexpected programmer errors (e.g., failures in `__str__`) and silently return `default`, which makes debugging harder. Prefer catching only the conversion-related exceptions (`ValueError`, `TypeError`) that `float()`/`str()` actually raise.

The special-casing of `bool` also changes Python’s default behavior (`float(True) == 1.0`, `float(False) == 0.0`). If the goal is to treat booleans as invalid numeric input, consider a short comment or a more explicit function name to document that choice; otherwise you could remove this branch to match standard numeric coercion semantics.

```suggestion
def coerce_float(value: Any, default: float | None = None) -> float | None:
    """Attempt to coerce a loosely-typed value to a float-like number."""
    if value is None:
        return default
    try:
        # Treat booleans as invalid numeric input instead of coercing to 0/1.
        if isinstance(value, bool):
            return default
        if isinstance(value, (int, float)):
            return float(value)
        s = str(value).strip()
        if s.lower() in ("", "none", "null"):
            return default
        return float(s)
    except (TypeError, ValueError):
        return default
```
</issue_to_address>

### Comment 2
<location> `MCPForUnity/Editor/Tools/ManageVFX.cs:1061-1070` </location>
<code_context>
+        private static object VFXListTemplates(JObject @params)
</code_context>

<issue_to_address>
**issue (bug_risk):** Template path comparisons mix absolute and asset-relative paths, so duplicate filtering may not work.

In `VFXListTemplates`, `relativePath = file.Replace("\\", "/")` is still an absolute filesystem path, while `AssetDatabase.GUIDToAssetPath(guid)` returns project-relative paths (`Assets/...`). The duplicate check:

```csharp
if (!templates.Any(t => ((dynamic)t).path == path))
```
compares these mismatched formats, so the same asset can be added twice.

Normalize paths before storing/comparing (e.g., convert filesystem paths to project-relative by stripping the project root, or convert all asset paths to absolute paths) so deduplication works reliably and `path` has a consistent format for consumers.
</issue_to_address>

### Comment 3
<location> `Server/src/services/tools/manage_vfx.py:216-217` </location>
<code_context>
+    reset_seed_on_play: Annotated[Any, "[VFX] Reset seed on play (bool or string)"] = None,
+    
+    # === LINE/TRAIL RENDERER PARAMETERS ===
+    positions: Annotated[Any, "[Line] Positions [[x,y,z], ...] (array or JSON string)"] = None,
+    position: Annotated[Any, "[Line/Trail] Single position [x,y,z] (array or JSON string)"] = None,
+    index: Annotated[Any, "[Line] Position index (integer or string)"] = None,
+    
</code_context>

<issue_to_address>
**issue (bug_risk):** Python-side docs allow JSON strings for arrays/vectors, but the C# side only handles arrays/objects.

Several parameters are documented as accepting "array or JSON string" (e.g. `positions`, `position`, `color`, `gradient`), but the C# handlers expect `JArray`/`JObject` (e.g. `@params["positions"] as JArray`, checks in `ParseColor`/`ParseVector3`/`ParseGradient`). If a client passes a JSON string, it will be treated as a string value in the `JObject`, those casts/checks will fail, and youll hit errors like "Positions array required".

Id recommend either parsing string values into JSON on the Python side before sending, or updating the Python annotations/docs to require structured JSON (arrays/objects) only, so the documented shapes match what the C# side actually accepts.
</issue_to_address>

### Comment 4
<location> `Server/src/services/tools/manage_vfx.py:296-318` </location>
<code_context>
+    """Unified VFX management tool."""
+    
+    # Validate action against known actions
+    if action not in ALL_ACTIONS:
+        # Provide helpful error with closest matches by prefix
+        prefix = action.split("_")[0] + "_" if "_" in action else ""
+        available_by_prefix = {
+            "particle_": PARTICLE_ACTIONS,
+            "vfx_": VFX_ACTIONS,
+            "line_": LINE_ACTIONS,
+            "trail_": TRAIL_ACTIONS,
+        }
+        suggestions = available_by_prefix.get(prefix, [])
+        if suggestions:
+            return {"success": False, "message": f"Unknown action '{action}'. Available {prefix}* actions: {', '.join(suggestions)}"}
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Action validation is case-sensitive in Python while the Unity side normalizes actions to lowercase.

On the C# side (`HandleCommand`), the action is lowercased (`actionLower = action.ToLowerInvariant()`) before matching, but here you validate the raw `action` against `ALL_ACTIONS`. That means inputs like `"Particle_Play"` or `"VFX_Set_Float"` would fail validation in Python even though Unity would accept them.

To align behavior, normalize to lowercase before validation and use that value going forward:

```python
action_normalized = action.lower()
if action_normalized not in ALL_ACTIONS:
    ...
params_dict = {"action": action_normalized}
```

Since `ALL_ACTIONS` is already lowercase, this should be a minimal change that keeps both sides consistent.

```suggestion
) -> dict[str, Any]:
    """Unified VFX management tool."""

    # Normalize action to lowercase to match Unity-side behavior
    action_normalized = action.lower()

    # Validate action against known actions using normalized value
    if action_normalized not in ALL_ACTIONS:
        # Provide helpful error with closest matches by prefix
        prefix = action_normalized.split("_")[0] + "_" if "_" in action_normalized else ""
        available_by_prefix = {
            "particle_": PARTICLE_ACTIONS,
            "vfx_": VFX_ACTIONS,
            "line_": LINE_ACTIONS,
            "trail_": TRAIL_ACTIONS,
        }
        suggestions = available_by_prefix.get(prefix, [])
        if suggestions:
            return {
                "success": False,
                "message": f"Unknown action '{action}'. Available {prefix}* actions: {', '.join(suggestions)}",
            }
        else:
            return {
                "success": False,
                "message": (
                    f"Unknown action '{action}'. Use prefixes: "
                    "particle_*, vfx_*, line_*, trail_*. Run with action='ping' to test connection."
                ),
            }

    unity_instance = get_unity_instance_from_context(ctx)

    # Build parameters dict with normalized action to stay consistent with Unity
    params_dict: dict[str, Any] = {"action": action_normalized}
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +216 to +217
positions: Annotated[Any, "[Line] Positions [[x,y,z], ...] (array or JSON string)"] = None,
position: Annotated[Any, "[Line/Trail] Single position [x,y,z] (array or JSON string)"] = None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Python-side docs allow JSON strings for arrays/vectors, but the C# side only handles arrays/objects.

Several parameters are documented as accepting "array or JSON string" (e.g. positions, position, color, gradient), but the C# handlers expect JArray/JObject (e.g. @params["positions"] as JArray, checks in ParseColor/ParseVector3/ParseGradient). If a client passes a JSON string, it will be treated as a string value in the JObject, those casts/checks will fail, and you’ll hit errors like "Positions array required".

I’d recommend either parsing string values into JSON on the Python side before sending, or updating the Python annotations/docs to require structured JSON (arrays/objects) only, so the documented shapes match what the C# side actually accepts.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @MCPForUnity/Editor/Tools/ManageVFX.cs:
- Around line 1909-1916: TrailClear currently calls FindTrailRenderer and
tr.Clear() without recording an undo; update TrailClear to call
UnityEditor.Undo.RecordObject (or Undo.RegisterCompleteObjectUndo) on the
TrailRenderer instance (use a label like "Clear Trail") prior to invoking
TrailRenderer.Clear() so the action is undoable in the Editor; keep the existing
success/failure return shape and consider updating the success message to
mention that runtime trail data may not be fully restorable via Undo because
TrailRenderer.Clear() affects non-serialized runtime state.

In @Server/src/services/tools/manage_vfx.py:
- Around line 449-450: The params_dict currently uses the same key "size" for
two different inputs (size and size_over_lifetime), causing size_over_lifetime
to silently overwrite size; update both assignments to use distinct keys (e.g.,
params_dict["size"] for the event-level size and params_dict["sizeOverLifetime"]
or params_dict["size_over_lifetime"] for the particle-system lifetime size)
wherever size_over_lifetime is set (the second occurrence around the later
block) and adjust any downstream consumers to read the new key (or preserve the
original "size" when appropriate to maintain backward compatibility), ensuring
both params_dict["size"] and the new params_dict["sizeOverLifetime"] are used
consistently.
🧹 Nitpick comments (4)
Server/src/services/tools/manage_vfx.py (1)

604-615: Nil-filtering is redundant but harmless.

Line 605 filters out None values, but all values are already guarded by if ... is not None checks before being added to params_dict. The filter is defensive and doesn't hurt, but could be removed for minor cleanup.

MCPForUnity/Editor/Tools/ManageVFX.cs (3)

751-755: Array size mismatch in ParticleAddBurst.

GetBursts is called with an array of size idx + 1, but only idx bursts exist at that point. Unity's GetBursts will only fill the first idx elements, leaving bursts[idx] uninitialized until you assign it.

While this works because you immediately assign bursts[idx] = burst, the intent would be clearer if the array is sized to idx first:

🔎 Suggested clearer approach
             int idx = emission.burstCount;
-            var bursts = new ParticleSystem.Burst[idx + 1];
-            emission.GetBursts(bursts);
+            var existingBursts = new ParticleSystem.Burst[idx];
+            emission.GetBursts(existingBursts);
+            var bursts = new ParticleSystem.Burst[idx + 1];
+            existingBursts.CopyTo(bursts, 0);
             bursts[idx] = burst;
             emission.SetBursts(bursts);

Or simply append after retrieving:

var bursts = new List<ParticleSystem.Burst>();
var temp = new ParticleSystem.Burst[idx];
emission.GetBursts(temp);
bursts.AddRange(temp);
bursts.Add(burst);
emission.SetBursts(bursts.ToArray());

985-995: Empty catch block silently swallows exceptions.

The empty catch { } at line 994 hides any errors that occur during template file searching. This could make debugging difficult if template discovery fails unexpectedly.

🔎 Suggested fix: Log or handle the exception
                 try
                 {
                     string[] allVfxFiles = System.IO.Directory.GetFiles(basePath, "*.vfx", System.IO.SearchOption.AllDirectories);
                     foreach (string file in allVfxFiles)
                     {
                         if (System.IO.Path.GetFileNameWithoutExtension(file).ToLower().Contains(templateName.ToLower()))
                             return file;
                     }
                 }
-                catch { }
+                catch (Exception ex)
+                {
+                    // Log but continue searching other paths
+                    Debug.LogWarning($"Error searching VFX templates in {basePath}: {ex.Message}");
+                }

1085-1097: Empty catch block and potential runtime exception with dynamic.

Two concerns:

  1. Line 1096: Empty catch { } silently swallows exceptions during template enumeration.
  2. Line 1104: Using dynamic for the path comparison ((dynamic)t).path can throw RuntimeBinderException if the anonymous type doesn't have a path property.
🔎 Suggested fixes

For the empty catch:

                 catch { }
+                catch (Exception ex)
+                {
+                    Debug.LogWarning($"Error enumerating VFX files in {basePath}: {ex.Message}");
+                }

For the dynamic usage, consider storing templates as a typed structure:

// Define a simple record or class
record VFXTemplateInfo(string Name, string Path, string Source);

// Use typed list
var templates = new List<VFXTemplateInfo>();
// ...
templates.Add(new VFXTemplateInfo(name, relativePath, isPackage ? "package" : "project"));

// Then comparison becomes safe
if (!templates.Any(t => t.Path == path))
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5511a2b and a534bb6.

📒 Files selected for processing (4)
  • MCPForUnity/Editor/Tools/ManageVFX.cs
  • MCPForUnity/Editor/Tools/ManageVFX.cs.meta
  • Server/src/services/tools/manage_vfx.py
  • Server/src/services/tools/utils.py
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-03T16:00:55.839Z
Learnt from: dsarno
Repo: CoplayDev/unity-mcp PR: 0
File: :0-0
Timestamp: 2025-09-03T16:00:55.839Z
Learning: ComponentResolver in UnityMcpBridge/Editor/Tools/ManageGameObject.cs is a nested static class within ManageGameObject, not a sibling type. The `using static MCPForUnity.Editor.Tools.ManageGameObject;` import is required to access ComponentResolver methods directly without the outer class qualifier.

Applied to files:

  • MCPForUnity/Editor/Tools/ManageVFX.cs
📚 Learning: 2025-12-29T15:23:11.613Z
Learnt from: msanatan
Repo: CoplayDev/unity-mcp PR: 491
File: MCPForUnity/Editor/Windows/EditorPrefs/EditorPrefsWindow.cs:78-115
Timestamp: 2025-12-29T15:23:11.613Z
Learning: In MCPForUnity, prefer relying on the established testing process to catch UI initialization issues instead of adding defensive null checks for UI elements in editor windows. This means during reviews, verify that tests cover UI initialization paths and that code avoids repetitive null-check boilerplate in Editor Windows. If a UI element can be null, ensure there is a well-tested fallback or that its initialization is guaranteed by design, rather than sprinkling null checks throughout editor code.

Applied to files:

  • MCPForUnity/Editor/Tools/ManageVFX.cs
🧬 Code graph analysis (2)
Server/src/services/tools/manage_vfx.py (2)
Server/src/transport/unity_transport.py (1)
  • send_with_unity_instance (79-113)
Server/src/transport/legacy/unity_connection.py (1)
  • async_send_command_with_retry (838-870)
MCPForUnity/Editor/Tools/ManageVFX.cs (1)
MCPForUnity/Editor/Helpers/ObjectResolver.cs (2)
  • Material (133-140)
  • Texture (145-152)
🪛 Ruff (0.14.10)
Server/src/services/tools/utils.py

93-93: Do not catch blind exception: Exception

(BLE001)

Server/src/services/tools/manage_vfx.py

42-42: Consider iterable unpacking instead of concatenation

Replace with iterable unpacking

(RUF005)

🔇 Additional comments (9)
MCPForUnity/Editor/Tools/ManageVFX.cs.meta (1)

1-13: LGTM!

Standard Unity .meta file with appropriate MonoImporter settings. The GUID appears manually crafted but is valid and will function correctly.

Server/src/services/tools/utils.py (1)

80-95: LGTM!

The implementation correctly mirrors coerce_int and handles all expected input types robustly. The broad except Exception is appropriate here since this is a tolerant coercion utility designed to fail gracefully to the default value on any parsing error.

Server/src/services/tools/manage_vfx.py (1)

126-296: LGTM - Well-structured tool with comprehensive parameter coverage.

The function signature is large but necessarily so for exposing a unified VFX API. The action validation with prefix-based suggestions provides good UX for users who mistype actions.

MCPForUnity/Editor/Tools/ManageVFX.cs (6)

78-281: LGTM - Robust parsing helpers.

The parsing methods handle multiple input formats (arrays, objects, scalars) with sensible defaults. The consistent pattern across ParseColor, ParseVector3, ParseVector4, ParseGradient, and ParseAnimationCurve makes the code predictable and maintainable.


286-310: LGTM - Clean action routing for ParticleSystem.

The switch statement cleanly maps action names to handler methods. The error message at line 308 lists all valid actions, which is helpful for debugging.


1188-1227: LGTM - VFX parameter setters are well-structured.

The generic VFXSetParameter<T> pattern is clean and allows type-safe parameter setting. The VFXSetVector method elegantly handles different vector dimensions with a single switch.


1384-1733: LGTM - Comprehensive LineRenderer implementation.

The shape creation methods (LineCreateCircle, LineCreateArc, LineCreateBezier) are mathematically correct. The cross-product calculation at lines 1640-1643 correctly handles the edge case where the normal is parallel to forward vector.


1736-1907: LGTM - TrailRenderer handlers are well-implemented.

Good use of conditional compilation for TrailEmit (Unity 2021.1+ API). The property setters follow the same consistent pattern as LineRenderer.


15-76: LGTM - Well-structured command dispatcher.

The HandleCommand method cleanly routes actions based on prefixes. The try-catch at lines 36-75 ensures exceptions are caught and returned with stack traces for debugging. Good use of ToLowerInvariant() for case-insensitive matching.

Comment on lines +449 to +450
if size is not None:
params_dict["size"] = size
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential parameter collision: size and size_over_lifetime both map to the same key.

Both parameters map to params_dict["size"]:

  • Line 450: size parameter → params_dict["size"] = size
  • Line 596: size_over_lifetime parameter → params_dict["size"] = size_over_lifetime

If both are provided, size_over_lifetime will overwrite size since it's processed later. This may be intentional (different contexts), but could cause confusion.

🔎 Suggested fix: Use distinct keys

Consider using sizeOverLifetime as a separate key if these serve different purposes:

     if size_over_lifetime is not None:
-        params_dict["size"] = size_over_lifetime
+        params_dict["sizeOverLifetime"] = size_over_lifetime

Or document that size is for VFX events while size_over_lifetime is for particle systems.

Also applies to: 595-596

🤖 Prompt for AI Agents
In @Server/src/services/tools/manage_vfx.py around lines 449 - 450, The
params_dict currently uses the same key "size" for two different inputs (size
and size_over_lifetime), causing size_over_lifetime to silently overwrite size;
update both assignments to use distinct keys (e.g., params_dict["size"] for the
event-level size and params_dict["sizeOverLifetime"] or
params_dict["size_over_lifetime"] for the particle-system lifetime size)
wherever size_over_lifetime is set (the second occurrence around the later
block) and adjust any downstream consumers to read the new key (or preserve the
original "size" when appropriate to maintain backward compatibility), ensuring
both params_dict["size"] and the new params_dict["sizeOverLifetime"] are used
consistently.

@dsarno
Copy link
Collaborator

dsarno commented Jan 7, 2026

@Scriptwonder this looks awesome, it'll be amazing to have a vfx tool.

I haven't looked too close at the code but for recent PRs I've been trying to do two things:

  1. really build them alongside comprehensive stress tests so from the beginning they come out strong and well tested and protected from regression

And

  1. try to avoid any redundancy or "wheel reinventing" by asking models to use any existing helpers and utilities in the package (parsing, resolution, serializing) etc.

That way we can not only avoid duplication but keep making the utilities and helpers stronger too.

Just some thoughts as we go! The models are getting so good that we can get a lot of this code hygiene basically for free just by asking them while we develop.

@Scriptwonder
Copy link
Collaborator Author

@Scriptwonder this looks awesome, it'll be amazing to have a vfx tool.

I haven't looked too close at the code but one thing I've been trying to do for recent PRs are two things:

  1. really build them alongside comprehensive stress tests so from the beginning they come out strong and well tested and protected from regression

And

  1. try to avoid any redundancy or "wheel reinventing" by asking models to use any existing helpers and utilities in the package (parsing, resolution, serializing) etc.

That way we can not only avoid duplication but keep making the utilities and helpers stronger too.

Just some thoughts as we go! The models are getting so good that we can get a lot of this code hygiene basically for free just by asking them while we develop.

Maybe a benchmark or a .rule/.skill file revolving around these? I have tried to avoid redundancy (even worse is the repetitive generation), but it still takes some labor and careful checking to do so.

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
@dsarno
Copy link
Collaborator

dsarno commented Jan 7, 2026

Maybe a benchmark or a .rule/.skill file revolving around these? I have tried to avoid redundancy (even worse is the repetitive generation), but it still takes some labor and careful checking to do so.

Yeah that's a good way to do it so it's basically automatic and agent-enforced. So not only do our coding agents refer to our architecture rules and existing utilities while we develop the repo, but they also have them for code review after they do a first draft, which I find super helpful (well before coderabbit feedback).

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @Server/src/services/tools/manage_vfx.py:
- Line 357: The issue is that start_color and start_color_line both write to
params_dict["startColor"] so the latter silently overwrites the former; fix by
using distinct keys or conditional assignment: either write start_color_line
into a separate key (e.g., params_dict["startColorLine"] or
params_dict["lineStartColor"]) or only set params_dict["startColor"] for the
appropriate renderer type by checking the component/type before assigning (use
the same condition that differentiates Particle/VFX vs Line/Trail), and update
any downstream consumers to read the new key or respect the conditional so
values don't collide.
🧹 Nitpick comments (1)
Server/src/services/tools/manage_vfx.py (1)

42-42: Optional: Use iterable unpacking for cleaner list construction.

The current list concatenation is correct, but using iterable unpacking [..., *PARTICLE_ACTIONS, ...] can be slightly more idiomatic.

🔎 Proposed refactor
-ALL_ACTIONS = ["ping"] + PARTICLE_ACTIONS + VFX_ACTIONS + LINE_ACTIONS + TRAIL_ACTIONS
+ALL_ACTIONS = ["ping", *PARTICLE_ACTIONS, *VFX_ACTIONS, *LINE_ACTIONS, *TRAIL_ACTIONS]
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e3a108 and 365d7d7.

📒 Files selected for processing (1)
  • Server/src/services/tools/manage_vfx.py
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: JohanHoltby
Repo: CoplayDev/unity-mcp PR: 309
File: MCPForUnity/Editor/Helpers/ServerInstaller.cs:478-508
Timestamp: 2025-10-13T13:41:00.086Z
Learning: In the MCPForUnityTools feature (MCPForUnity/Editor/Helpers/ServerInstaller.cs), the design intentionally forces users to have only one .py file per MCPForUnityTools folder to keep file tracking simple. Package-style tools (subdirectories with __init__.py) are not supported.
🪛 Ruff (0.14.10)
Server/src/services/tools/manage_vfx.py

42-42: Consider iterable unpacking instead of concatenation

Replace with iterable unpacking

(RUF005)

🔇 Additional comments (3)
Server/src/services/tools/manage_vfx.py (3)

45-125: Excellent documentation.

The comprehensive docstring clearly explains prerequisites, targeting options, and action groups. This will greatly help users understand component requirements before attempting VFX operations.


299-326: Action validation correctly normalizes to lowercase.

The implementation properly normalizes the action to lowercase before validation, ensuring consistency with Unity's case-insensitive action handling. The prefix-based error suggestions are helpful.


617-627: Clean parameter filtering and error handling.

The None-value filtering before sending and result normalization ensure clean data transfer and consistent return types.

if start_rotation is not None:
params_dict["startRotation"] = start_rotation
if start_color is not None:
params_dict["startColor"] = start_color
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Parameter collision: start_color_line overwrites start_color.

Both start_color (line 357, for Particle/VFX) and start_color_line (line 496, for Line/Trail) map to params_dict["startColor"]. If both are provided, start_color_line will silently overwrite start_color since it's processed later.

🔎 Suggested fix: Use distinct keys or document mutual exclusivity

Option 1: Use different keys for line/trail renderers:

     if start_color_line is not None:
-        params_dict["startColor"] = start_color_line
+        params_dict["lineStartColor"] = start_color_line

Option 2: Document that these parameters are mutually exclusive based on component type (Particle/VFX vs Line/Trail).

Also applies to: 357-357

Also applies to: 496-496

🤖 Prompt for AI Agents
In @Server/src/services/tools/manage_vfx.py at line 357, The issue is that
start_color and start_color_line both write to params_dict["startColor"] so the
latter silently overwrites the former; fix by using distinct keys or conditional
assignment: either write start_color_line into a separate key (e.g.,
params_dict["startColorLine"] or params_dict["lineStartColor"]) or only set
params_dict["startColor"] for the appropriate renderer type by checking the
component/type before assigning (use the same condition that differentiates
Particle/VFX vs Line/Trail), and update any downstream consumers to read the new
key or respect the conditional so values don't collide.

Added 6 helper methods :
ApplyCommonRendererProperties - Handles shadows, lighting, probes, sorting, rendering layer (used by ParticleSetRenderer, LineSetProperties, TrailSetProperties)
GetCommonRendererInfo - Returns common renderer info for GetInfo methods
ApplyWidthProperties - Generic width handling (used by LineSetWidth, TrailSetWidth)
ApplyColorProperties - Generic color handling with fadeEndAlpha option (used by LineSetColor, TrailSetColor)
SetRendererMaterial - Generic material assignment (used by LineSetMaterial, TrailSetMaterial)
ApplyLineTrailProperties - Shared Line/Trail properties like alignment, textureMode, numCornerVertices (used by LineSetProperties, TrailSetProperties)
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 7, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@msanatan
Copy link
Member

msanatan commented Jan 7, 2026

@Scriptwonder I just updated main, primarily to help with asset store uploads. I made some changes to the README, you might want to pull in the latest main before adding this tool to the README.

Also, can you split the ManageVFX logic into a few different files? If it's possible, it would be easier to read and update!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Fix all issues with AI agents
In @MCPForUnity/Editor/Helpers/RendererHelpers.cs:
- Around line 157-164: The code invokes several Action delegates without null
checks leading to possible NullReferenceExceptions; update the block that reads
@params keys so each delegate is null-checked before invocation (mirror the
existing checks for setLoop and setUseWorldSpace): ensure setNumCornerVertices,
setNumCapVertices, setAlignment, setTextureMode, and setGenerateLightingData are
each tested for null (e.g., "&& setNumCornerVertices != null") before calling
them and before adding their corresponding string to changes, and keep the
existing Enum.TryParse logic for setAlignment and setTextureMode.

In @MCPForUnity/Editor/Helpers/VectorParsing.cs:
- Around line 274-277: Multiple parsing methods use Debug.LogWarning instead of
the project logging helper; replace Debug.LogWarning calls in the catch blocks
of ParseVector4 (the catch with ex and message "[VectorParsing] Failed to parse
Vector4 from '{token}': {ex.Message}") and similarly for ParseGradient and
ParseAnimationCurve with McpLog.Warn, passing the same formatted message and the
exception (or include ex.Message) to maintain the same content and behavior;
locate the catch blocks inside the methods ParseVector4, ParseGradient, and
ParseAnimationCurve and swap Debug.LogWarning(...) to McpLog.Warn(...) so
logging is consistent with ParseVector3/ParseColor/ParseRect.
- Around line 232-236: The ParseColorOrDefault method incorrectly checks
defaultValue == default(Color) which treats transparent black as "unspecified";
change the API to accept a nullable Color (Nullable<Color> or Color?) or provide
an overload so callers can omit the default, then inside ParseColorOrDefault
check if defaultValue.HasValue and use defaultValue.Value otherwise use
Color.white; finally return ParseColor(token) ?? chosenDefault (or equivalent).
Ensure you update the method signature (ParseColorOrDefault) and usage sites to
pass a nullable Color or call the new overload, and keep ParseColor(token) as
the source of parsed value.

In @MCPForUnity/Editor/Tools/ManageVFX.cs:
- Around line 1646-1653: TrailClear does not record an Undo before mutating the
TrailRenderer; update the TrailClear method to call Undo.RecordObject(tr, "Clear
Trail") (or Undo.RegisterCompleteObjectUndo) on the TrailRenderer instance found
via FindTrailRenderer before calling tr.Clear(), then proceed to clear and
return the same result; also add a brief comment in the method noting that
TrailRenderer.Clear affects runtime-only trail data and may not be fully
restorable via Undo.
- Around line 889-903: The code builds a mix of absolute filesystem paths
(relativePath from file) and project-relative asset paths
(AssetDatabase.GUIDToAssetPath(guid)), causing duplicate-check mismatches in
VFXListTemplates; fix by normalizing the path stored in templates to a
project-relative form before adding and before comparing: convert file (the
absolute path) to a path that starts with "Assets/..." (e.g., use
UnityEditor.FileUtil.GetProjectRelativePath(file) or trim Application.dataPath
and prepend "Assets", then replace backslashes) and apply the same replacement
when checking packageInfo.resolvedPath so the isPackage test and the
templates.Any(t => ((dynamic)t).path == path) comparison use the same path
format.
- Around line 1412-1420: The LineRenderer is set to loop=true but you allocate
segments+1 positions and write both position 0 and position segments as the same
point, creating a duplicate vertex; fix by allocating exactly segments positions
(set lr.positionCount = segments) and change the loop to iterate i from 0 to i <
segments (compute angle as (float)i / segments * Mathf.PI * 2f) so you call
lr.SetPosition for indices 0..segments-1, avoiding the duplicate while keeping
lr.loop = true (references: lr, segments, positionCount, loop, SetPosition).
- Around line 687-718: Reflection calls to
UnityEditor.VFX.VisualEffectAssetEditorUtility.CreateNewAsset and
UnityEditor.VFX.VisualEffectResource.CreateNewAsset are fragile across VFX
Graph/Unity versions; update the code to (1) add a brief comment/guard
documenting supported VFX Graph package versions, (2) add explicit logs that
record which creation route succeeded (e.g.,
"VisualEffectAssetEditorUtility.CreateNewAsset succeeded" or
"VisualEffectResource.CreateNewAsset succeeded"), (3) replace the generic
failure message with specific diagnostics describing which reflection lookups
failed (e.g., "VisualEffectAssetEditorUtility type not found" / "CreateNewAsset
method not found on VisualEffectResource"), and (4) implement a stable fallback
using AssetDatabase.CreateAsset on a ScriptableObject-based VisualEffectAsset
placeholder if both reflection attempts fail, so code locates the logic in the
block referencing VisualEffectAssetEditorUtility, VisualEffectResource,
CreateNewAsset and AssetDatabase.LoadAssetAtPath.
🧹 Nitpick comments (2)
MCPForUnity/Editor/Tools/ManageVFX.cs (2)

550-554: Burst array initialization creates uninitialized slot before setting.

The array is created with idx + 1 slots, but GetBursts only fills idx slots (the existing bursts). The code then overwrites bursts[idx] with the new burst. This works correctly but is slightly confusing.

A clearer approach would be to create an array of exactly idx size for GetBursts, then create the final array:

🔎 Optional clarification
             int idx = emission.burstCount;
-            var bursts = new ParticleSystem.Burst[idx + 1];
-            emission.GetBursts(bursts);
+            var existingBursts = new ParticleSystem.Burst[idx];
+            emission.GetBursts(existingBursts);
+            var bursts = new ParticleSystem.Burst[idx + 1];
+            Array.Copy(existingBursts, bursts, idx);
             bursts[idx] = burst;
             emission.SetBursts(bursts);

1-26: Consider splitting this file for maintainability.

As noted in PR comments by msanatan, this file is quite large (~1670 lines). Consider splitting into separate files per component type (e.g., ManageVFX.ParticleSystem.cs, ManageVFX.LineRenderer.cs) using partial classes.

This would improve readability and make it easier for multiple contributors to work on different components simultaneously.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 365d7d7 and 14c0d55.

📒 Files selected for processing (4)
  • MCPForUnity/Editor/Helpers/RendererHelpers.cs
  • MCPForUnity/Editor/Helpers/RendererHelpers.cs.meta
  • MCPForUnity/Editor/Helpers/VectorParsing.cs
  • MCPForUnity/Editor/Tools/ManageVFX.cs
✅ Files skipped from review due to trivial changes (1)
  • MCPForUnity/Editor/Helpers/RendererHelpers.cs.meta
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: JohanHoltby
Repo: CoplayDev/unity-mcp PR: 309
File: MCPForUnity/Editor/Helpers/ServerInstaller.cs:478-508
Timestamp: 2025-10-13T13:41:00.086Z
Learning: In the MCPForUnityTools feature (MCPForUnity/Editor/Helpers/ServerInstaller.cs), the design intentionally forces users to have only one .py file per MCPForUnityTools folder to keep file tracking simple. Package-style tools (subdirectories with __init__.py) are not supported.
📚 Learning: 2025-12-29T15:23:11.613Z
Learnt from: msanatan
Repo: CoplayDev/unity-mcp PR: 491
File: MCPForUnity/Editor/Windows/EditorPrefs/EditorPrefsWindow.cs:78-115
Timestamp: 2025-12-29T15:23:11.613Z
Learning: In MCPForUnity, prefer relying on the established testing process to catch UI initialization issues instead of adding defensive null checks for UI elements in editor windows. This means during reviews, verify that tests cover UI initialization paths and that code avoids repetitive null-check boilerplate in Editor Windows. If a UI element can be null, ensure there is a well-tested fallback or that its initialization is guaranteed by design, rather than sprinkling null checks throughout editor code.

Applied to files:

  • MCPForUnity/Editor/Tools/ManageVFX.cs
  • MCPForUnity/Editor/Helpers/VectorParsing.cs
  • MCPForUnity/Editor/Helpers/RendererHelpers.cs
🧬 Code graph analysis (3)
MCPForUnity/Editor/Tools/ManageVFX.cs (3)
MCPForUnity/Editor/Helpers/VectorParsing.cs (12)
  • Color (178-226)
  • Color (232-236)
  • VectorParsing (12-531)
  • Vector3 (19-52)
  • Vector3 (57-60)
  • Vector4 (245-280)
  • Vector4 (286-289)
  • Gradient (300-373)
  • Gradient (379-391)
  • AnimationCurve (403-451)
  • AnimationCurve (459-462)
  • Vector2 (67-98)
MCPForUnity/Editor/Helpers/ObjectResolver.cs (2)
  • ObjectResolver (13-200)
  • Texture (146-153)
MCPForUnity/Editor/Helpers/RendererHelpers.cs (4)
  • RendererHelpers (13-166)
  • ApplyCommonRendererProperties (20-45)
  • SetRendererMaterial (130-145)
  • ApplyLineTrailProperties (151-164)
MCPForUnity/Editor/Helpers/VectorParsing.cs (1)
MCPForUnity/Editor/Tools/ManageVFX.cs (4)
  • Color (81-81)
  • Vector4 (83-83)
  • Gradient (84-84)
  • AnimationCurve (85-86)
MCPForUnity/Editor/Helpers/RendererHelpers.cs (2)
MCPForUnity/Editor/Helpers/VectorParsing.cs (6)
  • AnimationCurve (403-451)
  • AnimationCurve (459-462)
  • Color (178-226)
  • Color (232-236)
  • Gradient (300-373)
  • Gradient (379-391)
MCPForUnity/Editor/Tools/ManageVFX.cs (4)
  • AnimationCurve (85-86)
  • Color (81-81)
  • Gradient (84-84)
  • Material (91-92)
🔇 Additional comments (7)
MCPForUnity/Editor/Tools/ManageVFX.cs (2)

28-76: Well-structured command routing with proper error handling.

The HandleCommand method provides clean action routing with prefix-based dispatching. The try-catch wrapper and informative error messages are good practices for a tool API.


78-93: Good use of delegate wrappers for parsing utilities.

The parsing delegates cleanly delegate to VectorParsing and ObjectResolver, keeping the tool focused on VFX logic while reusing shared helpers. This aligns with the PR's goal of consolidating utilities.

MCPForUnity/Editor/Helpers/VectorParsing.cs (2)

300-373: Gradient parsing handles edge cases well.

The ParseGradient method supports both simple (startColor/endColor) and full (colorKeys/alphaKeys) formats, with sensible defaults when keys are missing. The alpha override via startAlpha/endAlpha is a nice touch for flexibility.


403-451: AnimationCurve parsing is comprehensive.

Good support for multiple input formats: constant values, simple start/end, and full keyframe arrays with tangent control. The alias handling (start/startValue, end/endValue) improves API usability.

MCPForUnity/Editor/Helpers/RendererHelpers.cs (3)

20-45: Clean extraction of common renderer property handling.

This consolidates shadow, lighting, probe, motion vector, and sorting layer logic that would otherwise be duplicated across ParticleSystemRenderer, LineRenderer, and TrailRenderer handlers. Good use of Enum.TryParse for safe enum handling.


75-91: Width property application is well-designed.

The callback-based approach allows both LineRenderer and TrailRenderer to share this logic while handling their specific property setters. The support for both unified width and separate startWidth/endWidth is user-friendly.


130-145: SetRendererMaterial properly handles Undo and dirty marking.

Good defensive checks for null renderer and missing path. The Undo recording before modification ensures the operation is reversible.

Comment on lines 687 to 718
// Create empty VFX asset using reflection to access internal API
try
{
// Try to use VisualEffectAssetEditorUtility.CreateNewAsset if available
var utilityType = System.Type.GetType("UnityEditor.VFX.VisualEffectAssetEditorUtility, Unity.VisualEffectGraph.Editor");
if (utilityType != null)
{
var createMethod = utilityType.GetMethod("CreateNewAsset", BindingFlags.Public | BindingFlags.Static);
if (createMethod != null)
{
createMethod.Invoke(null, new object[] { assetPath });
AssetDatabase.Refresh();
newAsset = AssetDatabase.LoadAssetAtPath<UnityEngine.VFX.VisualEffectAsset>(assetPath);
}
}

// Fallback: Create a ScriptableObject-based asset
if (newAsset == null)
{
// Try direct creation via internal constructor
var resourceType = System.Type.GetType("UnityEditor.VFX.VisualEffectResource, Unity.VisualEffectGraph.Editor");
if (resourceType != null)
{
var createMethod = resourceType.GetMethod("CreateNewAsset", BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
if (createMethod != null)
{
var resource = createMethod.Invoke(null, new object[] { assetPath });
AssetDatabase.Refresh();
newAsset = AssetDatabase.LoadAssetAtPath<UnityEngine.VFX.VisualEffectAsset>(assetPath);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the full context around the code in question
cat -n MCPForUnity/Editor/Tools/ManageVFX.cs | sed -n '660,740p'

Repository: CoplayDev/unity-mcp

Length of output: 4732


🏁 Script executed:

# Check if there are any version guards or conditional compilation around this code
cat -n MCPForUnity/Editor/Tools/ManageVFX.cs | sed -n '680,720p'

Repository: CoplayDev/unity-mcp

Length of output: 2660


🏁 Script executed:

# Look for any existing version checking patterns in the file
rg "UNITY_.*_OR_NEWER|EditorUserBuildSettingsUtils|Version" MCPForUnity/Editor/Tools/ManageVFX.cs -A 2 -B 2

Repository: CoplayDev/unity-mcp

Length of output: 280


🌐 Web query:

Unity VFX Graph API stability VisualEffectAsset CreateNewAsset breaking changes between versions

💡 Result:

Short answer: there is no stable, documented public API named VisualEffectAsset.CreateNewAsset in Unity’s supported VFX Graph API. Unity exposes VisualEffectAsset (constructor and read-only members) and the Visual Effect component runtime API; creating assets is done via UnityEditor APIs (AssetDatabase.CreateAsset) or the VFX package/editor utilities, and the VFX package has had multiple internal/editor changes across versions — so relying on an undocumented CreateNewAsset (or internal editor method) is likely to break between package/Editor versions. [1][2][3]

Actionable recommendations:

  • Use the supported APIs: create/save assets with UnityEditor.AssetDatabase.CreateAsset (or the ScriptableObject constructor + CreateAsset) and use the public VisualEffectAsset/VisualEffect component APIs at runtime. [1][2]
  • When upgrading Unity or the com.unity.visualeffectgraph package, read that package’s CHANGELOG for breaking or editor-side changes (every release documents notable fixes/changes). Check the changelog for the exact package version you’re moving to. [3][4]

Sources:

  • VisualEffectAsset (scripting API docs). [1]
  • Visual Effect component / Component API (VFX Graph manual). [2]
  • Visual Effect Graph package changelogs (example pages). [3][4]

If you want, tell me which Unity Editor version and Visual Effect Graph package version you’re targeting and I’ll check the changelogs and suggest the exact code pattern to create/save a VFX asset and notes about any breaking changes.


Reflection-based asset creation relies on undocumented internal APIs that change across Unity/VFX package versions.

The code at lines 687-718 uses reflection to access UnityEditor.VFX.VisualEffectAssetEditorUtility.CreateNewAsset and UnityEditor.VFX.VisualEffectResource.CreateNewAsset, neither of which are documented public APIs. The VFX Graph package has a history of internal/editor-side breaking changes across versions, making this approach fragile.

Consider:

  1. Document supported VFX Graph package versions and add a version guard or warning in comments
  2. Log which creation method succeeds (first or fallback) for debugging failed asset creation
  3. Improve error messages at line 728: replace the generic message with specific details about which reflection attempts failed (e.g., "VisualEffectAssetEditorUtility not found" vs "CreateNewAsset method not available")
  4. Consider using AssetDatabase.CreateAsset with a ScriptableObject subclass as a more stable fallback, since the internal APIs are undocumented and subject to change
🤖 Prompt for AI Agents
In @MCPForUnity/Editor/Tools/ManageVFX.cs around lines 687 - 718, Reflection
calls to UnityEditor.VFX.VisualEffectAssetEditorUtility.CreateNewAsset and
UnityEditor.VFX.VisualEffectResource.CreateNewAsset are fragile across VFX
Graph/Unity versions; update the code to (1) add a brief comment/guard
documenting supported VFX Graph package versions, (2) add explicit logs that
record which creation route succeeded (e.g.,
"VisualEffectAssetEditorUtility.CreateNewAsset succeeded" or
"VisualEffectResource.CreateNewAsset succeeded"), (3) replace the generic
failure message with specific diagnostics describing which reflection lookups
failed (e.g., "VisualEffectAssetEditorUtility type not found" / "CreateNewAsset
method not found on VisualEffectResource"), and (4) implement a stable fallback
using AssetDatabase.CreateAsset on a ScriptableObject-based VisualEffectAsset
placeholder if both reflection attempts fail, so code locates the logic in the
block referencing VisualEffectAssetEditorUtility, VisualEffectResource,
CreateNewAsset and AssetDatabase.LoadAssetAtPath.

Minor Fixes based on AI-feedback
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In @MCPForUnity/Editor/Helpers/VectorParsing.cs:
- Around line 445-448: The ParseAnimationCurve method is using Debug.LogWarning
instead of the project logging utility; replace the Debug.LogWarning call in the
catch block of ParseAnimationCurve with McpLog.Warn, passing the same formatted
message and exception details (matching the pattern used by
ParseVector3/ParseColor) so parsing failures are logged consistently via
McpLog.Warn including the token and ex.Message.
- Around line 367-370: Replace the inconsistent Debug.LogWarning call in
ParseGradient with the project-standard McpLog.Warn so the method uses the same
logging facility as ParseVector3 and ParseColor; locate the catch block in the
ParseGradient method that currently calls Debug.LogWarning($"[VectorParsing]
Failed to parse Gradient from '{token}': {ex.Message}") and change it to call
McpLog.Warn with the same message and error details.
- Around line 232-236: The method ParseColorOrDefault misdetects an omitted
default because comparing defaultValue to default compares against
Color(0,0,0,0) and will override any caller-supplied transparent black; change
the parameter to a nullable Color (e.g., Color? defaultValue = null) and then
treat null as "no default provided" by setting defaultValue = Color.white when
null, and return ParseColor(token) ?? defaultValue.Value; update the
ParseColorOrDefault signature and its null-check accordingly.

In @MCPForUnity/Editor/Tools/ManageVFX.cs:
- Around line 1440-1449: The circle creation sets LineRenderer.loop = true but
also writes segments + 1 positions, duplicating the start point; in the method
where lr is configured (uses lr.positionCount, lr.loop and the for loop calling
lr.SetPosition), change to use lr.positionCount = segments and iterate for (int
i = 0; i < segments; i++) computing angle = (float)i / segments * Mathf.PI * 2f
and SetPosition(i, point) so the renderer has exactly one vertex per segment
with loop=true (alternatively, keep segments+1 and set loop=false, but prefer
reducing to segments to remove the redundant duplicate).
- Around line 687-742: The reflection calls to
UnityEditor.VFX.VisualEffectAssetEditorUtility.CreateNewAsset and
UnityEditor.VFX.VisualEffectResource.CreateNewAsset rely on internal VFX Graph
APIs and need safer handling: add a short comment above the block listing the
supported VFX Graph package version(s) you tested, instrument each reflection
step with Debug.Log/Debug.LogWarning/Debug.LogError so you log which attempt ran
and whether it succeeded (log when utilityType is null, when createMethod is
null, when resourceType is null, etc.), and improve the final failure return
(and the catch return) to include which reflection attempts failed or the caught
exception message (e.g., indicate "VisualEffectAssetEditorUtility not found;
VisualEffectResource.CreateNewAsset failed: <error>"). Update the return paths
that currently return simple messages to include these specific diagnostics
while keeping the existing assetPath/assetName/template outputs on success.
🧹 Nitpick comments (2)
MCPForUnity/Editor/Tools/ManageVFX.cs (2)

467-478: Inconsistent material resolution pattern.

Lines 467-478 use ManageGameObject.FindObjectByInstruction to resolve materials, while the class defines a FindMaterialByPath helper (line 91-92) that delegates to ObjectResolver.ResolveMaterial. For consistency, consider using the same resolution pattern throughout the class.

🔎 Suggested refactor
             // Material
             if (@params["materialPath"] != null)
             {
-                var findInst = new JObject { ["find"] = @params["materialPath"].ToString() };
-                Material mat = ManageGameObject.FindObjectByInstruction(findInst, typeof(Material)) as Material;
+                Material mat = FindMaterialByPath(@params["materialPath"].ToString());
                 if (mat != null) { renderer.sharedMaterial = mat; changes.Add("material"); }
             }
             if (@params["trailMaterialPath"] != null)
             {
-                var findInst = new JObject { ["find"] = @params["trailMaterialPath"].ToString() };
-                Material mat = ManageGameObject.FindObjectByInstruction(findInst, typeof(Material)) as Material;
+                Material mat = FindMaterialByPath(@params["trailMaterialPath"].ToString());
                 if (mat != null) { renderer.trailMaterial = mat; changes.Add("trailMaterial"); }
             }

1098-1100: Inconsistent asset resolution pattern in VFX methods.

VFX methods use ManageGameObject.FindObjectByInstruction to resolve textures and meshes, creating temporary JObject wrappers. Consider defining helper methods similar to FindMaterialByPath for consistency across the codebase.

🔎 Suggested refactor

Add helper methods near the existing resolution helpers (around lines 88-92):

         private static Material FindMaterialByPath(string path) 
             => ObjectResolver.ResolveMaterial(path);
+        private static Texture FindTextureByPath(string path)
+            => ObjectResolver.ResolveTexture(path);

Then update the VFX methods:

         private static object VFXSetTexture(JObject @params)
         {
             // ...
-            var findInst = new JObject { ["find"] = path };
-            Texture tex = ManageGameObject.FindObjectByInstruction(findInst, typeof(Texture)) as Texture;
+            Texture tex = FindTextureByPath(path);
             // ...
         }

Apply similar changes for mesh resolution.

Also applies to: 1118-1120

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 14c0d55 and 2b12ac0.

📒 Files selected for processing (3)
  • MCPForUnity/Editor/Helpers/RendererHelpers.cs
  • MCPForUnity/Editor/Helpers/VectorParsing.cs
  • MCPForUnity/Editor/Tools/ManageVFX.cs
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: JohanHoltby
Repo: CoplayDev/unity-mcp PR: 309
File: MCPForUnity/Editor/Helpers/ServerInstaller.cs:478-508
Timestamp: 2025-10-13T13:41:00.086Z
Learning: In the MCPForUnityTools feature (MCPForUnity/Editor/Helpers/ServerInstaller.cs), the design intentionally forces users to have only one .py file per MCPForUnityTools folder to keep file tracking simple. Package-style tools (subdirectories with __init__.py) are not supported.
📚 Learning: 2025-12-29T15:23:11.613Z
Learnt from: msanatan
Repo: CoplayDev/unity-mcp PR: 491
File: MCPForUnity/Editor/Windows/EditorPrefs/EditorPrefsWindow.cs:78-115
Timestamp: 2025-12-29T15:23:11.613Z
Learning: In MCPForUnity, prefer relying on the established testing process to catch UI initialization issues instead of adding defensive null checks for UI elements in editor windows. This means during reviews, verify that tests cover UI initialization paths and that code avoids repetitive null-check boilerplate in Editor Windows. If a UI element can be null, ensure there is a well-tested fallback or that its initialization is guaranteed by design, rather than sprinkling null checks throughout editor code.

Applied to files:

  • MCPForUnity/Editor/Helpers/VectorParsing.cs
  • MCPForUnity/Editor/Helpers/RendererHelpers.cs
  • MCPForUnity/Editor/Tools/ManageVFX.cs
🧬 Code graph analysis (3)
MCPForUnity/Editor/Helpers/VectorParsing.cs (2)
MCPForUnity/Editor/Tools/ManageGameObject.cs (1)
  • Color (1840-1853)
MCPForUnity/Editor/Tools/ManageVFX.cs (4)
  • Color (81-81)
  • Vector4 (83-83)
  • Gradient (84-84)
  • AnimationCurve (85-86)
MCPForUnity/Editor/Helpers/RendererHelpers.cs (2)
MCPForUnity/Editor/Helpers/VectorParsing.cs (6)
  • AnimationCurve (403-451)
  • AnimationCurve (459-462)
  • Color (178-226)
  • Color (232-236)
  • Gradient (300-373)
  • Gradient (379-391)
MCPForUnity/Editor/Tools/ManageVFX.cs (4)
  • AnimationCurve (85-86)
  • Color (81-81)
  • Gradient (84-84)
  • Material (91-92)
MCPForUnity/Editor/Tools/ManageVFX.cs (5)
MCPForUnity/Editor/Helpers/VectorParsing.cs (12)
  • Color (178-226)
  • Color (232-236)
  • VectorParsing (12-531)
  • Vector3 (19-52)
  • Vector3 (57-60)
  • Vector4 (245-280)
  • Vector4 (286-289)
  • Gradient (300-373)
  • Gradient (379-391)
  • AnimationCurve (403-451)
  • AnimationCurve (459-462)
  • Vector2 (67-98)
MCPForUnity/Editor/Helpers/ObjectResolver.cs (2)
  • ObjectResolver (13-200)
  • Texture (146-153)
MCPForUnity/Editor/Tools/BatchExecute.cs (1)
  • JArray (153-161)
MCPForUnity/Editor/Helpers/RendererHelpers.cs (4)
  • RendererHelpers (13-166)
  • ApplyCommonRendererProperties (20-45)
  • SetRendererMaterial (130-145)
  • ApplyLineTrailProperties (151-164)
MCPForUnity/Editor/Tools/ManageAsset.cs (2)
  • CreateFolder (279-332)
  • DeleteAsset (495-523)
🔇 Additional comments (8)
MCPForUnity/Editor/Helpers/RendererHelpers.cs (6)

20-45: LGTM!

The ApplyCommonRendererProperties method correctly handles renderer properties with proper enum parsing, null checks, and change tracking.


50-62: LGTM!

The GetCommonRendererInfo method provides a clean snapshot of common renderer properties, consistent with the properties managed by ApplyCommonRendererProperties.


75-91: LGTM!

The ApplyWidthProperties method provides a clean abstraction for setting width-related properties. The required delegate parameters follow a consistent pattern throughout the helper class.


104-120: LGTM!

The ApplyColorProperties method provides flexible color configuration with the fadeEndAlpha parameter, which is useful for trail fade effects.


130-145: LGTM!

The SetRendererMaterial method handles material assignment with proper error handling, Undo support, and dirty marking.


151-164: LGTM!

The ApplyLineTrailProperties method correctly handles optional delegates with null checks, addressing the previously flagged issue. This allows the same helper to work for both LineRenderer (with loop/useWorldSpace) and TrailRenderer (without those properties).

MCPForUnity/Editor/Tools/ManageVFX.cs (2)

28-76: LGTM!

The HandleCommand method provides clean action routing with proper error handling. The prefix-based routing (particle_, vfx_, line_, trail_) keeps the code organized and extensible.


80-92: LGTM!

The helper delegates provide clean wrappers around VectorParsing and ObjectResolver, centralizing parsing and resolution logic for the entire tool.

Comment on lines 232 to 236
public static Color ParseColorOrDefault(JToken token, Color defaultValue = default)
{
if (defaultValue == default) defaultValue = Color.white;
return ParseColor(token) ?? defaultValue;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Unusual default value handling pattern.

The comparison if (defaultValue == default) for a Color struct compares against default(Color) which is (0,0,0,0) (transparent black), not Color.white. This means passing Color.black or any color with all zeros would unexpectedly be overridden to Color.white.

This was previously flagged and remains unresolved.

🔎 Suggested fix
-        public static Color ParseColorOrDefault(JToken token, Color defaultValue = default)
-        {
-            if (defaultValue == default) defaultValue = Color.white;
-            return ParseColor(token) ?? defaultValue;
-        }
+        /// <summary>
+        /// Parses a JToken into a Color, returning Color.white if parsing fails and no default is specified.
+        /// </summary>
+        public static Color ParseColorOrDefault(JToken token) => ParseColor(token) ?? Color.white;
+        
+        /// <summary>
+        /// Parses a JToken into a Color, returning the specified default if parsing fails.
+        /// </summary>
+        public static Color ParseColorOrDefault(JToken token, Color defaultValue) => ParseColor(token) ?? defaultValue;
🤖 Prompt for AI Agents
In @MCPForUnity/Editor/Helpers/VectorParsing.cs around lines 232 - 236, The
method ParseColorOrDefault misdetects an omitted default because comparing
defaultValue to default compares against Color(0,0,0,0) and will override any
caller-supplied transparent black; change the parameter to a nullable Color
(e.g., Color? defaultValue = null) and then treat null as "no default provided"
by setting defaultValue = Color.white when null, and return ParseColor(token) ??
defaultValue.Value; update the ParseColorOrDefault signature and its null-check
accordingly.

Comment on lines 687 to 742
// Create empty VFX asset using reflection to access internal API
try
{
// Try to use VisualEffectAssetEditorUtility.CreateNewAsset if available
var utilityType = System.Type.GetType("UnityEditor.VFX.VisualEffectAssetEditorUtility, Unity.VisualEffectGraph.Editor");
if (utilityType != null)
{
var createMethod = utilityType.GetMethod("CreateNewAsset", BindingFlags.Public | BindingFlags.Static);
if (createMethod != null)
{
createMethod.Invoke(null, new object[] { assetPath });
AssetDatabase.Refresh();
newAsset = AssetDatabase.LoadAssetAtPath<UnityEngine.VFX.VisualEffectAsset>(assetPath);
}
}

// Fallback: Create a ScriptableObject-based asset
if (newAsset == null)
{
// Try direct creation via internal constructor
var resourceType = System.Type.GetType("UnityEditor.VFX.VisualEffectResource, Unity.VisualEffectGraph.Editor");
if (resourceType != null)
{
var createMethod = resourceType.GetMethod("CreateNewAsset", BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
if (createMethod != null)
{
var resource = createMethod.Invoke(null, new object[] { assetPath });
AssetDatabase.Refresh();
newAsset = AssetDatabase.LoadAssetAtPath<UnityEngine.VFX.VisualEffectAsset>(assetPath);
}
}
}
}
catch (Exception ex)
{
return new { success = false, message = $"Failed to create VFX asset: {ex.Message}" };
}
}

if (newAsset == null)
{
return new { success = false, message = "Failed to create VFX asset. Try using a template from list_templates." };
}

return new
{
success = true,
message = $"Created VFX asset: {assetPath}",
data = new
{
assetPath = assetPath,
assetName = newAsset.name,
template = template
}
};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Reflection-based asset creation relies on undocumented internal APIs.

The code at lines 687-718 uses reflection to access UnityEditor.VFX.VisualEffectAssetEditorUtility.CreateNewAsset and UnityEditor.VFX.VisualEffectResource.CreateNewAsset, neither of which are documented public APIs. The VFX Graph package has a history of internal changes across versions.

This was previously flagged and remains unresolved. Consider:

  1. Document supported VFX Graph package versions in code comments
  2. Add logging when each creation method succeeds/fails
  3. Improve the error message at line 728 with specific details about which reflection attempts failed
🤖 Prompt for AI Agents
In @MCPForUnity/Editor/Tools/ManageVFX.cs around lines 687 - 742, The reflection
calls to UnityEditor.VFX.VisualEffectAssetEditorUtility.CreateNewAsset and
UnityEditor.VFX.VisualEffectResource.CreateNewAsset rely on internal VFX Graph
APIs and need safer handling: add a short comment above the block listing the
supported VFX Graph package version(s) you tested, instrument each reflection
step with Debug.Log/Debug.LogWarning/Debug.LogError so you log which attempt ran
and whether it succeeded (log when utilityType is null, when createMethod is
null, when resourceType is null, etc.), and improve the final failure return
(and the catch return) to include which reflection attempts failed or the caught
exception message (e.g., indicate "VisualEffectAssetEditorUtility not found;
VisualEffectResource.CreateNewAsset failed: <error>"). Update the return paths
that currently return simple messages to include these specific diagnostics
while keeping the existing assetPath/assetName/template outputs on success.

@Scriptwonder Scriptwonder merged commit d285c8d into CoplayDev:main Jan 7, 2026
1 check passed
@Scriptwonder
Copy link
Collaborator Author

@Scriptwonder I just updated main, primarily to help with asset store uploads. I made some changes to the README, you might want to pull in the latest main before adding this tool to the README.

Also, can you split the ManageVFX logic into a few different files? If it's possible, it would be easier to read and update!

Merged! Tested on both Windows/Mac on my end, looking good for the release. Tried to optimize the PR and will work on it more possibly later👍

@coderabbitai coderabbitai bot mentioned this pull request Jan 7, 2026
ZhangJiaLong90524 pushed a commit to ZhangJiaLong90524/unity-mcp-2020-compatible that referenced this pull request Jan 14, 2026
上游變更 (v7.0.0 → v9.0.3):
- GameObject 工具集重構 (CoplayDev#518)
- VFX 管理功能 (CoplayDev#520)
- 批次執行錯誤處理改進 (CoplayDev#531)
- Windows 長路徑問題修復 (CoplayDev#534)
- HTTP/Stdio 傳輸 UX 改進 (CoplayDev#530)
- LLM 工具註解功能 (CoplayDev#480)

衝突解決:
- modify/delete:接受刪除(架構已重構)
- content:接受 v9.0.3 版本(2020 相容性修正將另行處理)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants