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(); var cameraList = new List(); var unityCamList = new List(); // 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(); 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." }; } } }