Files
Tomicz Engineering LLC deea7d2a56 Replace reflection with version-gated conditional compilation.
Addresses review feedback on the Unity 6.5 GetInstanceID migration:

- UnityObjectIdCompatExtensions: drop the reflective method lookup in
  favor of a simple #if UNITY_6000_5_OR_NEWER / #else split calling
  GetEntityId() or GetInstanceID() directly. Also wrap the class in
  the MCPForUnity.Runtime.Helpers namespace.
- UnityTypeConverters: remove the reflective EntityIdToObject probe and
  call EditorUtility.EntityIdToObject(EntityId) directly under the same
  version gate. Serialize entityID as EntityId.ToULong() rather than
  ToString(), since Unity's docs explicitly warn that the textual form
  is not a stable serialization contract.
- Drop #pragma warning disable 0619 from 22 files that no longer make
  any direct calls to obsolete APIs. The remaining 7 files still need
  it (FindObjectsOfType, InstanceIDToObject fallback) and are left as-is
  — those deprecations are out of scope for this PR.
- Add the MCPForUnity.Runtime.Helpers using to every file that calls
  GetInstanceIDCompat() now that the extension method lives in a
  namespace.
2026-04-11 22:55:45 +02:00

232 lines
9.5 KiB
C#

using System;
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEngine;
using MCPForUnity.Editor.Helpers;
using MCPForUnity.Runtime.Helpers;
namespace MCPForUnity.Editor.Tools.Vfx
{
internal static class ParticleControl
{
public static object Create(JObject @params)
{
string target = @params["target"]?.ToString();
if (string.IsNullOrWhiteSpace(target))
{
return new { success = false, message = "target is required for particle_create" };
}
GameObject go = ManageVfxCommon.FindTargetGameObject(@params);
bool createdGameObject = false;
bool addedParticleSystem = false;
if (go == null)
{
string objectName = target;
int slashIndex = target.LastIndexOf('/');
if (slashIndex >= 0 && slashIndex < target.Length - 1)
{
objectName = target.Substring(slashIndex + 1);
}
go = new GameObject(objectName);
createdGameObject = true;
if (!EditorApplication.isPlaying)
{
Undo.RegisterCreatedObjectUndo(go, $"Create {objectName}");
}
}
if (@params["position"] != null)
{
go.transform.position = ManageVfxCommon.ParseVector3(@params["position"]);
}
if (@params["rotation"] != null)
{
go.transform.eulerAngles = ManageVfxCommon.ParseVector3(@params["rotation"]);
}
if (@params["scale"] != null)
{
go.transform.localScale = ManageVfxCommon.ParseVector3(@params["scale"]);
}
var ps = go.GetComponent<ParticleSystem>();
if (ps == null)
{
ps = go.AddComponent<ParticleSystem>();
addedParticleSystem = true;
// Apply sensible defaults so newly created particles aren't oversized.
RendererHelpers.SetSensibleParticleDefaults(ps);
}
var renderer = go.GetComponent<ParticleSystemRenderer>();
if (renderer != null)
{
RendererHelpers.EnsureMaterial(renderer);
}
// Allow caller overrides for playOnAwake and looping.
var main = ps.main;
if (@params["playOnAwake"] != null)
{
main.playOnAwake = @params["playOnAwake"].ToObject<bool>();
}
if (@params["looping"] != null)
{
main.loop = @params["looping"].ToObject<bool>();
}
EditorUtility.SetDirty(go);
if (!EditorApplication.isPlaying)
{
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(
UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
}
return new
{
success = true,
message = $"ParticleSystem ready on '{go.name}'",
target = go.name,
targetId = go.GetInstanceIDCompat(),
createdGameObject,
addedParticleSystem,
assignedMaterial = renderer?.sharedMaterial?.name
};
}
public static object EnableModule(JObject @params)
{
ParticleSystem ps = ParticleCommon.FindParticleSystem(@params);
if (ps == null) return new { success = false, message = ParticleCommon.FindParticleSystemError(@params) };
string moduleName = @params["module"]?.ToString()?.ToLowerInvariant();
bool enabled = @params["enabled"]?.ToObject<bool>() ?? true;
if (string.IsNullOrEmpty(moduleName)) return new { success = false, message = "Module name required" };
Undo.RecordObject(ps, $"Toggle {moduleName}");
switch (moduleName.Replace("_", ""))
{
case "emission": var em = ps.emission; em.enabled = enabled; break;
case "shape": var sh = ps.shape; sh.enabled = enabled; break;
case "coloroverlifetime": var col = ps.colorOverLifetime; col.enabled = enabled; break;
case "sizeoverlifetime": var sol = ps.sizeOverLifetime; sol.enabled = enabled; break;
case "velocityoverlifetime": var vol = ps.velocityOverLifetime; vol.enabled = enabled; break;
case "noise": var n = ps.noise; n.enabled = enabled; break;
case "collision": var coll = ps.collision; coll.enabled = enabled; break;
case "trails": var tr = ps.trails; tr.enabled = enabled; break;
case "lights": var li = ps.lights; li.enabled = enabled; break;
default: return new { success = false, message = $"Unknown module: {moduleName}" };
}
EditorUtility.SetDirty(ps);
return new { success = true, message = $"Module '{moduleName}' {(enabled ? "enabled" : "disabled")}" };
}
public static object Control(JObject @params, string action)
{
ParticleSystem ps = ParticleCommon.FindParticleSystem(@params);
if (ps == null) return new { success = false, message = ParticleCommon.FindParticleSystemError(@params) };
RendererHelpers.EnsureMaterialResult ensureResult = default;
bool materialChecked = false;
// Ensure material is assigned before playing
if (action == "play" || action == "restart")
{
var renderer = ParticleCommon.FindParticleSystemRenderer(ps);
if (renderer != null)
{
ensureResult = RendererHelpers.EnsureMaterial(renderer);
materialChecked = true;
}
}
bool withChildren = @params["withChildren"]?.ToObject<bool>() ?? true;
switch (action)
{
case "play": ps.Play(withChildren); break;
case "stop": ps.Stop(withChildren, ParticleSystemStopBehavior.StopEmitting); break;
case "pause": ps.Pause(withChildren); break;
case "restart": ps.Stop(withChildren, ParticleSystemStopBehavior.StopEmittingAndClear); ps.Play(withChildren); break;
case "clear": ps.Clear(withChildren); break;
default: return new { success = false, message = $"Unknown action: {action}" };
}
return new
{
success = true,
message = $"ParticleSystem {action}",
materialReplaced = materialChecked ? ensureResult.MaterialReplaced : false,
replacementReason = materialChecked ? ensureResult.ReplacementReason : string.Empty,
};
}
public static object AddBurst(JObject @params)
{
ParticleSystem ps = ParticleCommon.FindParticleSystem(@params);
if (ps == null) return new { success = false, message = ParticleCommon.FindParticleSystemError(@params) };
// Ensure material is assigned
var renderer = ParticleCommon.FindParticleSystemRenderer(ps);
RendererHelpers.EnsureMaterialResult ensureResult = default;
bool materialChecked = false;
if (renderer != null)
{
ensureResult = RendererHelpers.EnsureMaterial(renderer);
materialChecked = true;
}
Undo.RecordObject(ps, "Add Burst");
var emission = ps.emission;
float time = @params["time"]?.ToObject<float>() ?? 0f;
int minCountRaw = @params["minCount"]?.ToObject<int>() ?? @params["count"]?.ToObject<int>() ?? 30;
int maxCountRaw = @params["maxCount"]?.ToObject<int>() ?? @params["count"]?.ToObject<int>() ?? 30;
short minCount = (short)Math.Clamp(minCountRaw, 0, short.MaxValue);
short maxCount = (short)Math.Clamp(maxCountRaw, 0, short.MaxValue);
int cycles = @params["cycles"]?.ToObject<int>() ?? 1;
float interval = @params["interval"]?.ToObject<float>() ?? 0.01f;
var burst = new ParticleSystem.Burst(time, minCount, maxCount, cycles, interval);
burst.probability = @params["probability"]?.ToObject<float>() ?? 1f;
int idx = emission.burstCount;
var bursts = new ParticleSystem.Burst[idx + 1];
emission.GetBursts(bursts);
bursts[idx] = burst;
emission.SetBursts(bursts);
EditorUtility.SetDirty(ps);
return new
{
success = true,
message = $"Added burst at t={time}",
burstIndex = idx,
materialReplaced = materialChecked ? ensureResult.MaterialReplaced : false,
replacementReason = materialChecked ? ensureResult.ReplacementReason : string.Empty,
};
}
public static object ClearBursts(JObject @params)
{
ParticleSystem ps = ParticleCommon.FindParticleSystem(@params);
if (ps == null) return new { success = false, message = ParticleCommon.FindParticleSystemError(@params) };
Undo.RecordObject(ps, "Clear Bursts");
var emission = ps.emission;
int count = emission.burstCount;
emission.SetBursts(new ParticleSystem.Burst[0]);
EditorUtility.SetDirty(ps);
return new { success = true, message = $"Cleared {count} bursts" };
}
}
}