Files
Shutong Wu c56337ec03 refactor: drop defensive scaffolding made obsolete by module dep declarations (#1122)
Since #1122 declared the Physics 2D, Screen Capture, Image Conversion, etc.
built-in modules as required deps in package.json, Unity Package Manager now
keeps them enabled while MCP for Unity is installed. Two defensive layers
that protected against the now-impossible "module disabled" case become dead
code, plus one sub-floor preprocessor gate that was always-true on our
declared `unity: 2021.3` floor.

Tier 1A — UnityPhysicsCompat.cs
- Revert `Type.GetType("UnityEngine.Physics2D, UnityEngine.Physics2DModule")`
  to `typeof(Physics2D)`. The property-level reflection (for `autoSyncTransforms`
  deprecation in Unity 6.x) stays — that's a different concern.

Tier 1B — TrailControl.cs
- Strip `#if UNITY_2021_1_OR_NEWER` gate. Package floor is 2021.3 per
  package.json, so the `#else` branch ("AddPosition requires Unity 2021.1+")
  is unreachable.

Tier 2 — ScreenshotUtility.cs + callers
- Remove `IsScreenCaptureModuleAvailable` property, `ScreenCaptureModuleNotAvailableError`
  constant, `InvokeCaptureScreenshotAsTexture` helper, and the
  `s_captureScreenshotMethod` / `s_captureScreenshotAsTextureMethod` /
  `s_screenCaptureModuleAvailable` reflection caches.
- Replace reflective `MethodInfo.Invoke` calls with direct `ScreenCapture.X`
  calls in `CaptureToProjectFolder`, `CaptureComposited`, and the
  `ScreenshotCapturer` MonoBehaviour.
- Camera-based fallback path (`FindAvailableCamera` + `CaptureFromCameraToProjectFolder`)
  is preserved — it still handles transient null returns from `ScreenCapture`
  inside `CaptureComposited`.
- Drop pre-flight `if (!IsScreenCaptureModuleAvailable)` gates in
  `ManageUI.cs` (UI render) and `ManageScene.cs` (screenshot path).
- `ProjectInfo` resource: `screenCapture` field is now an invariant `true`
  (preserves API shape for `mcpforunity://project/info` consumers).

Net: 144 lines removed, 15 added. No new tests — the changes are pure
removal of code paths that cannot fire with the current deps declared.

Related: #1160, #1122.
2026-05-26 16:13:00 +08:00

262 lines
9.4 KiB
C#

using System;
using System.Reflection;
using UnityEngine;
namespace MCPForUnity.Runtime.Helpers
{
// Part of MCP for Unity's compat-shim family. See UnityCompatShims.cs in this
// folder for the full list of shims, the audit policy, and the reflection pattern.
/// <summary>
/// Version-compatible wrappers for Physics / Physics2D properties whose surface
/// changes across Unity versions.
///
/// Currently covered:
/// - Physics.autoSyncTransforms (deprecated in Unity 6.x; replacement is Physics.SyncTransforms())
/// - Physics2D.autoSyncTransforms (deprecated in Unity 6.x; replacement is Physics2D.SyncTransforms())
/// - Physics.autoSimulation (deprecated in 2022.2; replacement is Physics.simulationMode)
///
/// We use reflection rather than direct property access so calls stay clean of
/// CS0618 warnings AND survive eventual removal of the obsolete property without
/// a recompile of this package.
/// </summary>
public static class UnityPhysicsCompat
{
/// <summary>
/// Cross-version description of the 3D physics simulation mode.
/// On 2022.2+ this maps onto <c>UnityEngine.SimulationMode</c>; on older
/// versions it represents the on/off semantics of <c>autoSimulation</c>.
/// </summary>
public enum SimulationMode
{
FixedUpdate,
Update,
Script,
Unknown,
}
// ---------- Physics2D ----------
private static PropertyInfo _physics2DAutoSync;
private static bool _physics2DProbed;
private static PropertyInfo Physics2DAutoSyncProp
{
get
{
if (!_physics2DProbed)
{
_physics2DProbed = true;
_physics2DAutoSync = typeof(Physics2D).GetProperty(
"autoSyncTransforms",
BindingFlags.Public | BindingFlags.Static);
}
return _physics2DAutoSync;
}
}
/// <summary>
/// Reads <c>Physics2D.autoSyncTransforms</c> if the property exists in this
/// Unity version. Returns <c>null</c> if the property has been removed.
/// </summary>
public static bool? GetPhysics2DAutoSyncTransforms()
{
var prop = Physics2DAutoSyncProp;
if (prop == null || !prop.CanRead) return null;
try { return (bool)prop.GetValue(null); }
catch { return null; }
}
/// <summary>
/// Writes <c>Physics2D.autoSyncTransforms</c> if the property exists and is
/// writable. Returns <c>true</c> if the write happened, <c>false</c> if the
/// property is unavailable in this Unity version.
/// </summary>
public static bool TrySetPhysics2DAutoSyncTransforms(bool value)
{
var prop = Physics2DAutoSyncProp;
if (prop == null || !prop.CanWrite) return false;
try
{
prop.SetValue(null, value);
return true;
}
catch
{
return false;
}
}
// ---------- Physics (3D) ----------
private static PropertyInfo _physicsAutoSync;
private static bool _physicsProbed;
private static PropertyInfo PhysicsAutoSyncProp
{
get
{
if (!_physicsProbed)
{
_physicsProbed = true;
_physicsAutoSync = typeof(Physics).GetProperty(
"autoSyncTransforms",
BindingFlags.Public | BindingFlags.Static);
}
return _physicsAutoSync;
}
}
/// <summary>
/// Reads <c>Physics.autoSyncTransforms</c> if the property exists in this
/// Unity version. Returns <c>null</c> if the property has been removed.
/// </summary>
public static bool? GetPhysicsAutoSyncTransforms()
{
var prop = PhysicsAutoSyncProp;
if (prop == null || !prop.CanRead) return null;
try { return (bool)prop.GetValue(null); }
catch { return null; }
}
/// <summary>
/// Writes <c>Physics.autoSyncTransforms</c> if the property exists and is
/// writable. Returns <c>true</c> if the write happened, <c>false</c> if the
/// property is unavailable in this Unity version.
/// </summary>
public static bool TrySetPhysicsAutoSyncTransforms(bool value)
{
var prop = PhysicsAutoSyncProp;
if (prop == null || !prop.CanWrite) return false;
try
{
prop.SetValue(null, value);
return true;
}
catch
{
return false;
}
}
// ---------- Physics simulation mode (3D) ----------
// 2022.2+ : UnityEngine.Physics.simulationMode (UnityEngine.SimulationMode enum)
// <2022.2 : UnityEngine.Physics.autoSimulation (bool — true ⇔ FixedUpdate, false ⇔ Script)
private static PropertyInfo _physicsSimulationMode;
private static bool _physicsSimulationModeProbed;
private static PropertyInfo _physicsAutoSimulation;
private static bool _physicsAutoSimulationProbed;
private static PropertyInfo PhysicsSimulationModeProp
{
get
{
if (!_physicsSimulationModeProbed)
{
_physicsSimulationModeProbed = true;
_physicsSimulationMode = typeof(Physics).GetProperty(
"simulationMode",
BindingFlags.Public | BindingFlags.Static);
}
return _physicsSimulationMode;
}
}
private static PropertyInfo PhysicsAutoSimulationProp
{
get
{
if (!_physicsAutoSimulationProbed)
{
_physicsAutoSimulationProbed = true;
_physicsAutoSimulation = typeof(Physics).GetProperty(
"autoSimulation",
BindingFlags.Public | BindingFlags.Static);
}
return _physicsAutoSimulation;
}
}
/// <summary>
/// Reads the current 3D physics simulation mode in a Unity-version-agnostic way.
/// Returns <see cref="SimulationMode.Unknown"/> if neither API is available.
/// </summary>
public static SimulationMode GetPhysicsSimulationMode()
{
var modeProp = PhysicsSimulationModeProp;
if (modeProp != null && modeProp.CanRead)
{
try { return ParseSimulationMode(modeProp.GetValue(null)?.ToString()); }
catch { /* fall through */ }
}
var autoProp = PhysicsAutoSimulationProp;
if (autoProp != null && autoProp.CanRead)
{
try
{
var auto = (bool)autoProp.GetValue(null);
return auto ? SimulationMode.FixedUpdate : SimulationMode.Script;
}
catch { /* fall through */ }
}
return SimulationMode.Unknown;
}
/// <summary>
/// Sets the 3D physics simulation mode in a Unity-version-agnostic way.
/// Returns false if the requested mode isn't expressible on this Unity version
/// (e.g. Update mode on pre-2022.2).
/// </summary>
public static bool TrySetPhysicsSimulationMode(SimulationMode mode)
{
var modeProp = PhysicsSimulationModeProp;
if (modeProp != null && modeProp.CanWrite)
{
try
{
var enumType = modeProp.PropertyType;
var enumValue = Enum.Parse(enumType, mode.ToString(), ignoreCase: true);
modeProp.SetValue(null, enumValue);
return true;
}
catch { /* fall through to legacy bool path */ }
}
var autoProp = PhysicsAutoSimulationProp;
if (autoProp != null && autoProp.CanWrite)
{
try
{
switch (mode)
{
case SimulationMode.FixedUpdate:
autoProp.SetValue(null, true);
return true;
case SimulationMode.Script:
autoProp.SetValue(null, false);
return true;
// Update mode does not exist pre-2022.2 — caller must handle false.
}
}
catch { /* fall through */ }
}
return false;
}
private static SimulationMode ParseSimulationMode(string s)
{
if (string.IsNullOrEmpty(s)) return SimulationMode.Unknown;
switch (s.ToLowerInvariant())
{
case "fixedupdate": return SimulationMode.FixedUpdate;
case "update": return SimulationMode.Update;
case "script": return SimulationMode.Script;
default: return SimulationMode.Unknown;
}
}
}
}