316 lines
13 KiB
C#
316 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using MCPForUnity.Editor.Helpers;
|
|
using Newtonsoft.Json.Linq;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using MCPForUnity.Runtime.Helpers;
|
|
|
|
namespace MCPForUnity.Editor.Tools.Cameras
|
|
{
|
|
internal static class CameraControl
|
|
{
|
|
internal static object ListCameras(JObject @params)
|
|
{
|
|
var unityCameras = UnityFindObjectsCompat.FindAll<UnityEngine.Camera>();
|
|
var cameraList = new List<object>();
|
|
var unityCamList = new List<object>();
|
|
|
|
// Cinemachine cameras
|
|
if (CameraHelpers.HasCinemachine)
|
|
{
|
|
var cmType = CameraHelpers.CinemachineCameraType;
|
|
var allCm = UnityFindObjectsCompat.FindAll(cmType);
|
|
foreach (Component cm in allCm)
|
|
{
|
|
var follow = CameraHelpers.GetReflectionProperty(cm, "Follow") as Transform;
|
|
var lookAt = CameraHelpers.GetReflectionProperty(cm, "LookAt") as Transform;
|
|
var isLive = CameraHelpers.GetReflectionProperty(cm, "IsLive");
|
|
var priority = CameraHelpers.ReadCinemachinePriority(cm);
|
|
|
|
var body = CameraHelpers.GetPipelineComponent(cm, "Body");
|
|
var aim = CameraHelpers.GetPipelineComponent(cm, "Aim");
|
|
var noise = CameraHelpers.GetPipelineComponent(cm, "Noise");
|
|
|
|
// Collect extensions
|
|
var extensions = new List<string>();
|
|
var cmExtBaseType = cm.GetType().Assembly.GetType("Unity.Cinemachine.CinemachineExtension");
|
|
if (cmExtBaseType != null)
|
|
{
|
|
foreach (var comp in cm.gameObject.GetComponents(cmExtBaseType))
|
|
{
|
|
if (comp != null)
|
|
extensions.Add(comp.GetType().Name);
|
|
}
|
|
}
|
|
|
|
cameraList.Add(new
|
|
{
|
|
instanceID = cm.gameObject.GetInstanceIDCompat(),
|
|
name = cm.gameObject.name,
|
|
isLive = isLive is bool b && b,
|
|
priority,
|
|
follow = follow != null ? new { name = follow.gameObject.name, instanceID = follow.gameObject.GetInstanceIDCompat() } : null,
|
|
lookAt = lookAt != null ? new { name = lookAt.gameObject.name, instanceID = lookAt.gameObject.GetInstanceIDCompat() } : null,
|
|
body = body?.GetType().Name,
|
|
aim = aim?.GetType().Name,
|
|
noise = noise?.GetType().Name,
|
|
extensions
|
|
});
|
|
}
|
|
}
|
|
|
|
// Unity cameras
|
|
foreach (var cam in unityCameras)
|
|
{
|
|
bool hasBrain = CameraHelpers.HasCinemachine &&
|
|
cam.gameObject.GetComponent(CameraHelpers.CinemachineBrainType) != null;
|
|
unityCamList.Add(new
|
|
{
|
|
instanceID = cam.gameObject.GetInstanceIDCompat(),
|
|
name = cam.gameObject.name,
|
|
depth = cam.depth,
|
|
fieldOfView = cam.fieldOfView,
|
|
hasBrain
|
|
});
|
|
}
|
|
|
|
// Brain info
|
|
object brainInfo = null;
|
|
if (CameraHelpers.HasCinemachine)
|
|
{
|
|
var brain = CameraHelpers.FindBrain();
|
|
if (brain != null)
|
|
{
|
|
var activeCam = CameraHelpers.GetReflectionProperty(brain, "ActiveVirtualCamera");
|
|
var isBlending = CameraHelpers.GetReflectionProperty(brain, "IsBlending");
|
|
|
|
string activeName = null;
|
|
int? activeID = null;
|
|
if (activeCam != null)
|
|
{
|
|
var nameProp = activeCam.GetType().GetProperty("Name");
|
|
activeName = nameProp?.GetValue(activeCam) as string;
|
|
|
|
if (activeCam is Component activeComp)
|
|
activeID = activeComp.gameObject.GetInstanceIDCompat();
|
|
}
|
|
|
|
brainInfo = new
|
|
{
|
|
exists = true,
|
|
gameObject = brain.gameObject.name,
|
|
instanceID = brain.gameObject.GetInstanceIDCompat(),
|
|
activeCameraName = activeName,
|
|
activeCameraID = activeID,
|
|
isBlending = isBlending is bool bl && bl
|
|
};
|
|
}
|
|
}
|
|
|
|
return new
|
|
{
|
|
success = true,
|
|
data = new
|
|
{
|
|
brain = brainInfo,
|
|
cinemachineCameras = cameraList,
|
|
unityCameras = unityCamList,
|
|
cinemachineInstalled = CameraHelpers.HasCinemachine
|
|
}
|
|
};
|
|
}
|
|
|
|
internal static object GetBrainStatus(JObject @params)
|
|
{
|
|
var brain = CameraHelpers.FindBrain();
|
|
if (brain == null)
|
|
return new ErrorResponse("No CinemachineBrain found in the scene.");
|
|
|
|
var activeCam = CameraHelpers.GetReflectionProperty(brain, "ActiveVirtualCamera");
|
|
var isBlending = CameraHelpers.GetReflectionProperty(brain, "IsBlending");
|
|
var activeBlend = CameraHelpers.GetReflectionProperty(brain, "ActiveBlend");
|
|
|
|
string activeName = null;
|
|
int? activeID = null;
|
|
if (activeCam != null)
|
|
{
|
|
var nameProp = activeCam.GetType().GetProperty("Name");
|
|
activeName = nameProp?.GetValue(activeCam) as string;
|
|
if (activeCam is Component comp)
|
|
activeID = comp.gameObject.GetInstanceIDCompat();
|
|
}
|
|
|
|
string blendDesc = null;
|
|
if (activeBlend != null)
|
|
{
|
|
var descProp = activeBlend.GetType().GetProperty("Description");
|
|
blendDesc = descProp?.GetValue(activeBlend) as string;
|
|
}
|
|
|
|
return new
|
|
{
|
|
success = true,
|
|
data = new
|
|
{
|
|
gameObject = brain.gameObject.name,
|
|
instanceID = brain.gameObject.GetInstanceIDCompat(),
|
|
activeCameraName = activeName,
|
|
activeCameraID = activeID,
|
|
isBlending = isBlending is bool b && b,
|
|
blendDescription = blendDesc
|
|
}
|
|
};
|
|
}
|
|
|
|
internal static object SetBlend(JObject @params)
|
|
{
|
|
var brain = CameraHelpers.FindBrain();
|
|
if (brain == null)
|
|
return new ErrorResponse("No CinemachineBrain found. Use 'ensure_brain' first.");
|
|
|
|
var props = CameraHelpers.ExtractProperties(@params) ?? new JObject();
|
|
Undo.RecordObject(brain, "Set Camera Blend");
|
|
|
|
using var so = new SerializedObject(brain);
|
|
var defaultBlendProp = so.FindProperty("DefaultBlend") ?? so.FindProperty("m_DefaultBlend");
|
|
if (defaultBlendProp == null)
|
|
return new ErrorResponse("Could not find DefaultBlend property on CinemachineBrain.");
|
|
|
|
string style = ParamCoercion.CoerceString(props["style"], null);
|
|
if (style != null)
|
|
{
|
|
var styleProp = defaultBlendProp.FindPropertyRelative("Style")
|
|
?? defaultBlendProp.FindPropertyRelative("m_Style");
|
|
if (styleProp != null && styleProp.propertyType == SerializedPropertyType.Enum)
|
|
{
|
|
// Try to parse the style enum
|
|
var enumNames = styleProp.enumNames;
|
|
int idx = Array.FindIndex(enumNames, n => n.Equals(style, StringComparison.OrdinalIgnoreCase));
|
|
if (idx >= 0)
|
|
styleProp.enumValueIndex = idx;
|
|
}
|
|
}
|
|
|
|
float duration = ParamCoercion.CoerceFloat(props["duration"], -1f);
|
|
if (duration >= 0)
|
|
{
|
|
var timeProp = defaultBlendProp.FindPropertyRelative("Time")
|
|
?? defaultBlendProp.FindPropertyRelative("m_Time");
|
|
if (timeProp != null)
|
|
timeProp.floatValue = duration;
|
|
}
|
|
|
|
so.ApplyModifiedProperties();
|
|
CameraHelpers.MarkDirty(brain.gameObject);
|
|
|
|
return new
|
|
{
|
|
success = true,
|
|
message = "Default blend configured on CinemachineBrain.",
|
|
data = new { instanceID = brain.gameObject.GetInstanceIDCompat() }
|
|
};
|
|
}
|
|
|
|
private static int _overrideId = -1;
|
|
|
|
internal static object ForceCamera(JObject @params)
|
|
{
|
|
var brain = CameraHelpers.FindBrain();
|
|
if (brain == null)
|
|
return new ErrorResponse("No CinemachineBrain found. Use 'ensure_brain' first.");
|
|
|
|
var cmCamera = CameraHelpers.FindCinemachineCamera(@params);
|
|
if (cmCamera == null)
|
|
return new ErrorResponse("Target CinemachineCamera not found.");
|
|
|
|
// Use SetCameraOverride via reflection
|
|
var brainType = brain.GetType();
|
|
var method = brainType.GetMethod("SetCameraOverride",
|
|
BindingFlags.Public | BindingFlags.Instance);
|
|
|
|
if (method == null)
|
|
{
|
|
// Fallback: just set high priority
|
|
CameraHelpers.SetReflectionProperty(cmCamera, "Priority", 999);
|
|
return new
|
|
{
|
|
success = true,
|
|
message = $"Set high priority on '{cmCamera.gameObject.name}' (SetCameraOverride not available).",
|
|
data = new { instanceID = cmCamera.gameObject.GetInstanceIDCompat(), method = "priority" }
|
|
};
|
|
}
|
|
|
|
try
|
|
{
|
|
// CM3 signature: SetCameraOverride(int overrideId, int priority,
|
|
// ICinemachineCamera camA, ICinemachineCamera camB, float weightB, float deltaTime)
|
|
// -1 for overrideId creates a new override; same cam for A+B with weight=1 = instant switch
|
|
_overrideId = (int)method.Invoke(brain, new object[]
|
|
{
|
|
_overrideId >= 0 ? _overrideId : -1,
|
|
999, // high priority to win over all others
|
|
cmCamera, // camA (at weight=0)
|
|
cmCamera, // camB (at weight=1) — same camera = no blend
|
|
1f, // weightB = fully on camB
|
|
-1f // deltaTime = use default
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Fallback
|
|
CameraHelpers.SetReflectionProperty(cmCamera, "Priority", 999);
|
|
return new
|
|
{
|
|
success = true,
|
|
message = $"Forced via priority (override failed: {ex.Message}).",
|
|
data = new { instanceID = cmCamera.gameObject.GetInstanceIDCompat(), method = "priority" }
|
|
};
|
|
}
|
|
|
|
return new
|
|
{
|
|
success = true,
|
|
message = $"Camera overridden to '{cmCamera.gameObject.name}'.",
|
|
data = new
|
|
{
|
|
instanceID = cmCamera.gameObject.GetInstanceIDCompat(),
|
|
overrideId = _overrideId,
|
|
method = "override"
|
|
}
|
|
};
|
|
}
|
|
|
|
internal static object ReleaseOverride(JObject @params)
|
|
{
|
|
var brain = CameraHelpers.FindBrain();
|
|
if (brain == null)
|
|
return new ErrorResponse("No CinemachineBrain found.");
|
|
|
|
if (_overrideId < 0)
|
|
return new { success = true, message = "No active camera override to release." };
|
|
|
|
var method = brain.GetType().GetMethod("ReleaseCameraOverride",
|
|
BindingFlags.Public | BindingFlags.Instance);
|
|
|
|
if (method != null)
|
|
{
|
|
method.Invoke(brain, new object[] { _overrideId });
|
|
int releasedId = _overrideId;
|
|
_overrideId = -1;
|
|
return new
|
|
{
|
|
success = true,
|
|
message = "Camera override released.",
|
|
data = new { releasedOverrideId = releasedId }
|
|
};
|
|
}
|
|
|
|
_overrideId = -1;
|
|
return new { success = true, message = "Override state cleared." };
|
|
}
|
|
}
|
|
}
|