Files
Echo/Assets/_Project/Scripts/OpeningSystem/AwakeningSequenceController.cs
T
2026-07-08 20:42:51 +08:00

250 lines
8.9 KiB
C#

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<CharacterController>();
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;
}
}
}