250 lines
8.9 KiB
C#
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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|