using System; using System.Collections; using System.Collections.Generic; using AibisDream.Kit; using AibisDream.Utility; using Cinemachine; using DG.Tweening; using DG.Tweening.Core; using DG.Tweening.Plugins.Options; using UnityEngine; using UnityEngine.Rendering.Universal; using Yarn.Unity; namespace AibisDream.Framework { public class CameraKit : Singleton { private const int LowPriority = 1; private const int HighPriority = 10; #region 相机组件 public static Camera UICamera => UIManager.Instance.UICamera; private CinemachineBrain _orthoCameraBrain; private readonly Dictionary _virtualCameraDict = new(); private readonly Dictionary _camInitStateDict = new(); private CameraEnum _curCameraEnum; private CamState _curInitState; private Camera _orthoMainCamera; private Tween _shakeTween; private struct CamState { public Vector3 pos; public float initOrthoSize; } #endregion private CameraKit() { } public override void OnSingletonInit() { // 获取正交相机 _orthoMainCamera = Camera.main; _orthoCameraBrain = _orthoMainCamera?.GetComponent(); _curCameraEnum = CameraEnum.Default; } #region 相机信息注册 public void RegisterUICamera(Camera overlayCamera) { var mainCameraData = _orthoMainCamera.GetUniversalAdditionalCameraData(); if (mainCameraData != null) { if (!mainCameraData.cameraStack.Contains(overlayCamera)) { mainCameraData.cameraStack.Insert(0, overlayCamera); } } else { Debug.LogError("Main Camera does not have UniversalAdditionalCameraData."); } } public void UnRegisterUICamera(Camera overlayCamera) { // 获取 Main Camera 的 UniversalAdditionalCameraData var mainCameraData = _orthoMainCamera.GetUniversalAdditionalCameraData(); if (mainCameraData != null) { // 检查 Overlay Camera 是否在 Stack 中 if (mainCameraData.cameraStack.Contains(overlayCamera)) { // 将 Overlay Camera 从 Stack 移除 mainCameraData.cameraStack.Remove(overlayCamera); Debug.Log("Overlay Camera removed from Main Camera stack."); } } else { Debug.LogError("Main Camera does not have UniversalAdditionalCameraData."); } } public void RegisterCamera(CameraEnum camType, ICinemachineCamera virtualCamera) { _virtualCameraDict[camType] = virtualCamera; var vcam = virtualCamera.VirtualCameraGameObject.GetComponent(); if (vcam == null) { Debug.LogWarning($"RegisterCamera: {camType} 的 GameObject 上未找到 CinemachineVirtualCamera 组件"); return; } var initState = new CamState { pos = virtualCamera.VirtualCameraGameObject.transform.localPosition, initOrthoSize = vcam.m_Lens.OrthographicSize }; _camInitStateDict[camType] = initState; } public void UnRegisterCamera(CameraEnum camType) { _virtualCameraDict.Remove(camType); _camInitStateDict.Remove(camType); } public void RemoveAllCam() { _virtualCameraDict.Clear(); _camInitStateDict.Clear(); } #endregion /// /// 检查当前是否需要切相机 /// /// /// 是就不用切相机 public bool EqualToCameraState(CameraEnum targetCameraState) { return _curCameraEnum == targetCameraState; } /// /// 切换相机 /// /// 目标相机 /// true 则跳过过渡动画立即切换(适用于读档等场景) public IEnumerator SwitchCamera(CameraEnum camType, bool immediate = false) { var switchDuration = SwitchCameraSync(camType, immediate); if (switchDuration > 0f) { yield return new WaitForSeconds(switchDuration); } else { // 至少等待两帧,确保相机切换完成 yield return null; yield return null; } } private float SwitchCameraSync(CameraEnum camType, bool immediate = false) { if (_curCameraEnum == camType) { return 0; } if (!_virtualCameraDict.ContainsKey(camType)) { Debug.LogWarning($"SwitchCamera: {camType} 相机未注册"); return 0; } var activeBrain = _orthoCameraBrain; CinemachineBlendDefinition originalBlend = default; if (immediate) { originalBlend = activeBrain.m_DefaultBlend; activeBrain.m_DefaultBlend = new CinemachineBlendDefinition( CinemachineBlendDefinition.Style.Cut, 0f); } _virtualCameraDict.TryGetValue(_curCameraEnum, out var fromVCam); var toVCam = _virtualCameraDict[camType]; // 设置虚拟相机优先级 foreach (var camPair in _virtualCameraDict) { camPair.Value.Priority = camPair.Key == camType ? HighPriority : LowPriority; } _curInitState = _camInitStateDict[camType]; var lastCamEnum = _curCameraEnum; _curCameraEnum = camType; if (immediate) { ResetVirCam(lastCamEnum); var blend = originalBlend; ActionKit.Delay(0f, () => activeBrain.m_DefaultBlend = blend).StartGlobal(); return 0f; } // CinemachineBrain.LookupBlend 在 2.x 中是 private,这里重写其等价逻辑: // Custom Blends 资产里有匹配项就用它,否则回退到 Default Blend。 var blendTime = ResolveBlendTime(activeBrain, fromVCam, toVCam); ActionKit.Delay(blendTime, () => ResetVirCam(lastCamEnum)).StartGlobal(); return blendTime; } private static float ResolveBlendTime( CinemachineBrain brain, ICinemachineCamera fromCam, ICinemachineCamera toCam) { var blend = brain.m_DefaultBlend; if (brain.m_CustomBlends != null) { var fromName = fromCam != null ? fromCam.Name : string.Empty; var toName = toCam != null ? toCam.Name : string.Empty; blend = brain.m_CustomBlends.GetBlendForVirtualCameras(fromName, toName, blend); } if (CinemachineCore.GetBlendOverride != null) { blend = CinemachineCore.GetBlendOverride(fromCam, toCam, blend, brain); } return blend.BlendTime; } public void ResetCamera() { _curCameraEnum = CameraEnum.Default; RemoveAllCam(); } private void ResetVirCam(CameraEnum cameraEnum) { if (cameraEnum == CameraEnum.Default) { return; } if (!_virtualCameraDict.TryGetValue(cameraEnum, out var virCam)) { return; } var camState = _camInitStateDict[cameraEnum]; virCam.VirtualCameraGameObject.transform.localPosition = camState.pos; var camObj = virCam.VirtualCameraGameObject.GetComponent(); camObj.m_Lens.OrthographicSize = camState.initOrthoSize; } private Tween TransCamera(Vector3 targetPos, float targetOrthographicSize, float duration) { // 寻找现在正在激活的相机 if (_orthoCameraBrain == null) { Debug.LogWarning("TransCamera: CinemachineBrain is null."); return null; } var curCamera = _orthoCameraBrain.ActiveVirtualCamera; if (curCamera == null) { Debug.LogWarning("TransCamera: No active virtual camera found."); return null; } var sq = DOTween.Sequence(); // 移动 sq.Join(curCamera.DOLocalMove(targetPos, duration)); // 缩放 sq.Join(curCamera.DOOrthoSize(targetOrthographicSize, duration)); return sq; } private Tween ReturnInitCamera(float duration) { return TransCamera(_curInitState.pos, _curInitState.initOrthoSize, duration); } public IEnumerator TransCameraAsync(Vector3 targetPos, float targetOrthographicSize, float duration) { var tween = TransCamera(targetPos, targetOrthographicSize, duration); if (tween != null) { yield return tween.WaitForCompletion(); } } public IEnumerator ReturnInitCameraAsync(float duration) { var tween = ReturnInitCamera(duration); if (tween != null) { yield return tween.WaitForCompletion(); } } public IEnumerator ShakeCamera(float duration, float strength, int vibrate) { if (_orthoCameraBrain == null) { Debug.LogWarning("ShakeCamera: CinemachineBrain is null."); yield break; } var curCamera = _orthoCameraBrain.ActiveVirtualCamera; if (curCamera == null) { Debug.LogWarning("ShakeCamera: No active virtual camera found."); yield break; } var transform = curCamera.VirtualCameraGameObject.transform; // 停止并清理之前的震动,防止多个 tween 叠加导致无法回归原点 if (_shakeTween != null && _shakeTween.IsActive()) { _shakeTween.Kill(); if (_camInitStateDict.TryGetValue(_curCameraEnum, out var camState)) { transform.localPosition = camState.pos; } } _shakeTween = transform .DOShakePosition(duration, strength, vibrate, 90, false, false) .OnComplete(() => _shakeTween = null); yield return _shakeTween.WaitForCompletion(); } /// /// 获取当前激活的相机 /// public Camera GetCurrentCamera() { return _orthoMainCamera; } /// /// 获取鼠标在世界中的位置 /// /// 鼠标界面位置 /// 世界位置 public static Vector3 GetMouseWorldPos(Vector3 mousePoint) { var cam = Instance._orthoMainCamera; if (cam == null) return Vector3.zero; var pos = cam.ScreenToWorldPoint(mousePoint); return new Vector3(pos.x, pos.y, 0); } public static Vector3 GetScreenPos(Vector3 worldPos) { var cam = Instance._orthoMainCamera; if (cam == null) return Vector3.zero; worldPos.z = 0; return cam.WorldToScreenPoint(worldPos); } } public static class CinemachineExtension { public static TweenerCore DOLocalMove(this ICinemachineCamera camera, Vector3 targetPos, float duration) { var transform = camera.VirtualCameraGameObject.gameObject.transform; return transform.DOLocalMove(targetPos, duration); } public static TweenerCore DOOrthoSize(this ICinemachineCamera camera, float endValue, float duration) { var virtualCamera = camera.VirtualCameraGameObject.GetComponent(); var currentOrthoSize = virtualCamera.m_Lens.OrthographicSize; return DOTween.To(() => currentOrthoSize, x => currentOrthoSize = x, endValue, duration) .OnUpdate(() => virtualCamera.m_Lens.OrthographicSize = currentOrthoSize); } } public static class CameraCommand { [YarnCommand("return_init_camera")] public static IEnumerator ReturnInitCamera(float duration = 1f) { return CameraKit.Instance.ReturnInitCameraAsync(duration); } [YarnCommand("trans_camera")] public static IEnumerator TransCameraAsync(float x, float y, float targetOrthographicSize, float duration) { float z = CameraKit.Instance.GetCurrentCamera()?.transform.position.z ?? -10f; Vector3 targetPos = new Vector3(x, y, z); return CameraKit.Instance.TransCameraAsync(targetPos, targetOrthographicSize, duration); } [YarnCommand("shake_camera")] public static IEnumerator ShakeCamera(float duration, float strength, int vibrate = 10) { return CameraKit.Instance.ShakeCamera(duration, strength, vibrate); } [YarnCommand("switch_camera")] public static IEnumerator SwitchCamera(string targetStateStr, bool immediate = false) { if (!Enum.TryParse(targetStateStr, out var targetState)) { Debug.LogWarning($"SwitchCamera: \"{targetStateStr}\" 不在 CameraEnum 枚举中"); yield break; } yield return CameraKit.Instance.SwitchCamera(targetState, immediate); } } public enum CameraEnum { Default, Clinic, CoolingMachine, BodyModule, BodyModuleCenter, Memory, Logic, Eye, UF, Expression, EyeDeep, ExpressionDeep, Emo, EmoDeep, Op, CutEmo, CutMemory, CutLogic, CutEmoDeep, CutMemoryDeep, CutLogicDeep, BreakCamera, DreamCamera, Subway, SubwayTV, Language, LanguageDeep, AnalysisMode, AnalysisModeDeep, SaleCamera, SaleDeepCamera } }