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 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 UpdateCameraViewport(bool reset = false) { foreach (var overlayCam in _orthoMainCamera.GetUniversalAdditionalCameraData().cameraStack) { overlayCam.UpdateCameraViewport(reset); } _orthoMainCamera.UpdateCameraViewport(reset); } public void RegisterUICamera(Camera overlayCamera) { var mainCameraData = _orthoMainCamera.GetUniversalAdditionalCameraData(); if (mainCameraData != null) { if (!mainCameraData.cameraStack.Contains(overlayCamera)) { overlayCamera.rect = _orthoMainCamera.rect; 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); } } 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); } // 设置虚拟相机优先级 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; } ActionKit.Delay(activeBrain.m_DefaultBlend.BlendTime, () => ResetVirCam(lastCamEnum)).StartGlobal(); return activeBrain.m_DefaultBlend.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; } yield return curCamera.VirtualCameraGameObject.transform .DOShakePosition(duration, strength, vibrate, 90, false, false) .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 void UpdateCameraViewport(this Camera camera, bool reset = false) { if (reset) { camera.rect = new Rect(0, 0, 1, 1); return; } // 当前屏幕宽高比 float windowAspect = (float)Screen.width / (float)Screen.height; // 计算缩放比例 float scaleHeight = windowAspect / ConstRef.TargetAspect; Rect rect = camera.rect; if (scaleHeight < 1.0f) { // 添加黑边 rect.width = 1.0f; rect.height = scaleHeight; rect.x = 0; rect.y = (1.0f - scaleHeight) / 2.0f; } else { // 屏幕比目标更宽,左右添加黑边(pillarbox) rect.width = 1.0f / scaleHeight; rect.height = 1.0f; rect.x = (1.0f - 1.0f / scaleHeight) / 2.0f; rect.y = 0; } camera.rect = rect; } } 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 } }