using System.Collections; using System.IO; using UnityEngine; using Core.InputLock; using UnityEngine.Events; #if DOTWEEN using DG.Tweening; #endif namespace OpeningSystem { public class AwakeningSequenceController : MonoBehaviour { [Header("Trigger Conditions")] [Tooltip("如果为 true,则仅在没有存档文件时(即新游戏/重新开始)才会播放此动画")] public bool onlyPlayOnNewGame = true; [Header("UI Fader")] [Tooltip("拖入一个全屏纯黑 Image 的 CanvasGroup")] public CanvasGroup blackScreenCanvasGroup; [Header("Sequence Timing")] public float initialBlackDuration = 2f; public float fadeFromBlackDuration = 3f; public float cameraLookUpDuration = 4f; [Header("Look Staging")] [Range(0f, 1f)] public float lookMidProgress01 = 0.5f; public float lookMidPauseDuration = 0.25f; [Header("Player & Camera References")] public Transform playerBody; public Transform playerCameraPivot; [Header("Target Positions & Angles")] [Tooltip("主角醒来时的初始位置与朝向标记(如果为空则在主角原位置醒来)")] public Transform startPlayerTransform; [Tooltip("相机初始俯视角度(正数表示低头看地,例如 60f)")] public float cameraStartDownAngle = 60f; [Header("Events")] [SerializeField] private UnityEvent onAwakeningFinished = new UnityEvent(); [SerializeField] private bool invokeFinishedWhenSkipped = false; #if DOTWEEN [Header("DOTween")] public bool useUnscaledTime = false; public Ease fadeFromBlackEase = Ease.InOutSine; public Ease lookEase = Ease.InOutSine; #endif private void Start() { if (onlyPlayOnNewGame) { string savePath = Path.Combine(Application.persistentDataPath, "savegame.json"); if (File.Exists(savePath)) { // 这是一个"继续游戏"的情况,不播放苏醒动画 if (blackScreenCanvasGroup != null) { blackScreenCanvasGroup.alpha = 0f; blackScreenCanvasGroup.gameObject.SetActive(false); } if (invokeFinishedWhenSkipped) { onAwakeningFinished.Invoke(); } return; } } StartAwakeningSequence(); } public void StartAwakeningSequence() { #if DOTWEEN PlaySequenceWithDOTween(); #else StartCoroutine(AwakeningCoroutine()); #endif } #if DOTWEEN private void PlaySequenceWithDOTween() { if (PlayerControlLockService.Instance != null) { PlayerControlLockService.Instance.Lock(this); } if (blackScreenCanvasGroup == null) { Debug.LogWarning("[AwakeningSequenceController] Black Screen CanvasGroup 未指定!"); UnlockPlayer(); return; } // 初始化状态:纯黑、相机低头、传送玩家 blackScreenCanvasGroup.gameObject.SetActive(true); blackScreenCanvasGroup.alpha = 1f; ApplyStartPositionAndReset(); Sequence sequence = DOTween.Sequence(); sequence.SetUpdate(useUnscaledTime); // 1. 保持黑屏一段时间(昏迷中) sequence.AppendInterval(initialBlackDuration); // 2. 视野逐渐变亮(睁眼) Tween fadeFromTween = blackScreenCanvasGroup.DOFade(0f, Mathf.Max(0.0001f, fadeFromBlackDuration)).SetEase(fadeFromBlackEase); sequence.Append(fadeFromTween); // 3. 镜头慢慢抬起(站起/抬头) if (playerCameraPivot != null) { float mid01 = Mathf.Clamp01(lookMidProgress01); float firstDuration = Mathf.Max(0.0001f, cameraLookUpDuration * mid01); float secondDuration = Mathf.Max(0.0001f, cameraLookUpDuration * (1f - mid01)); float midAngle = Mathf.Lerp(cameraStartDownAngle, 0f, mid01); Sequence lookSequence = DOTween.Sequence(); lookSequence.SetUpdate(useUnscaledTime); lookSequence.Append(playerCameraPivot.DOLocalRotate(new Vector3(midAngle, 0f, 0f), firstDuration, RotateMode.Fast).SetEase(lookEase)); if (lookMidPauseDuration > 0f) lookSequence.AppendInterval(lookMidPauseDuration); lookSequence.Append(playerCameraPivot.DOLocalRotate(Vector3.zero, secondDuration, RotateMode.Fast).SetEase(lookEase)); sequence.Join(lookSequence); } // 4. 动画结束,解除锁定并隐藏黑屏UI sequence.AppendCallback(() => { blackScreenCanvasGroup.gameObject.SetActive(false); UnlockPlayer(); onAwakeningFinished.Invoke(); }); } #endif private IEnumerator AwakeningCoroutine() { if (PlayerControlLockService.Instance != null) { PlayerControlLockService.Instance.Lock(this); } if (blackScreenCanvasGroup == null) { Debug.LogWarning("[AwakeningSequenceController] Black Screen CanvasGroup 未指定!"); UnlockPlayer(); yield break; } // 初始化状态:纯黑、相机低头、传送玩家 blackScreenCanvasGroup.gameObject.SetActive(true); blackScreenCanvasGroup.alpha = 1f; ApplyStartPositionAndReset(); // 1. 保持黑屏一段时间(昏迷中) yield return new WaitForSeconds(initialBlackDuration); // 2 & 3. 视野逐渐变亮并伴随镜头抬起 Coroutine fadeRoutine = StartCoroutine(FadeScreen(1f, 0f, fadeFromBlackDuration, blackScreenCanvasGroup)); Coroutine lookRoutine = StartCoroutine(LookUpCoroutine()); yield return fadeRoutine; yield return lookRoutine; // 4. 动画结束,解除锁定并隐藏黑屏UI blackScreenCanvasGroup.gameObject.SetActive(false); UnlockPlayer(); onAwakeningFinished.Invoke(); } private void UnlockPlayer() { if (PlayerControlLockService.Instance != null) { PlayerControlLockService.Instance.Unlock(this); } } private void ApplyStartPositionAndReset() { if (playerBody != null && startPlayerTransform != null) { var cc = playerBody.GetComponent(); if (cc != null) cc.enabled = false; playerBody.position = startPlayerTransform.position; playerBody.rotation = startPlayerTransform.rotation; if (cc != null) cc.enabled = true; } if (playerCameraPivot != null) { playerCameraPivot.localRotation = Quaternion.Euler(cameraStartDownAngle, 0f, 0f); } } private IEnumerator LookUpCoroutine() { if (playerCameraPivot == null) yield break; float mid01 = Mathf.Clamp01(lookMidProgress01); float firstDuration = Mathf.Max(0.0001f, cameraLookUpDuration * mid01); float secondDuration = Mathf.Max(0.0001f, cameraLookUpDuration * (1f - mid01)); Quaternion startRot = playerCameraPivot.localRotation; Quaternion midRot = Quaternion.Euler(Mathf.Lerp(cameraStartDownAngle, 0f, mid01), 0f, 0f); Quaternion endRot = Quaternion.identity; float t = 0f; while (t < 1f) { t += Time.deltaTime / firstDuration; playerCameraPivot.localRotation = Quaternion.Lerp(startRot, midRot, t); yield return null; } playerCameraPivot.localRotation = midRot; if (lookMidPauseDuration > 0f) { yield return new WaitForSeconds(lookMidPauseDuration); } t = 0f; while (t < 1f) { t += Time.deltaTime / secondDuration; playerCameraPivot.localRotation = Quaternion.Lerp(midRot, endRot, t); yield return null; } playerCameraPivot.localRotation = endRot; } private IEnumerator FadeScreen(float startAlpha, float endAlpha, float duration, CanvasGroup cg) { float t = 0f; while (t < 1f) { t += Time.deltaTime / duration; cg.alpha = Mathf.Lerp(startAlpha, endAlpha, t); yield return null; } cg.alpha = endAlpha; } } }