Files
MRSnowWhite/Assets/_SnowWhite/Scripts/Cutscene/CrystalStatueEvent.cs

717 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using System.Collections;
using DarkTonic.MasterAudio;
using DG.Tweening;
using SpiritSystem;
/// <summary>
/// 水晶雕像事件 - 管理三轮水晶雕像挑战
/// 使用场景中预设的雕像组,不实例化生成
/// </summary>
public class CrystalStatueEvent : CutsceneBase
{
#region
[Header("雕像组配置")]
[Tooltip("第一轮雕像组 - 邪恶表情")]
public CrystalStatueGroup round1Statues;
[Tooltip("第二轮雕像组 - 恶毒双手")]
public CrystalStatueGroup round2Statues;
[Tooltip("第三轮雕像组 - 嫉妒之心")]
public CrystalStatueGroup round3Statues;
[Header("时间配置")]
[Tooltip("每轮时间限制(秒)")]
public float roundTimeLimit = 90f;
[Tooltip("轮次间隔时间")]
public float delayBetweenRounds = 2f;
[Header("音效配置")]
[Tooltip("第一轮介绍语音")]
public string audioRound1Intro = "1.5";
[Tooltip("第一轮成功语音")]
public string audioRound1Success = "1.7";
[Tooltip("第二轮介绍语音")]
public string audioRound2Intro = "1.8";
[Tooltip("第二轮成功语音")]
public string audioRound2Success = "1.9";
[Tooltip("第三轮介绍语音")]
public string audioRound3Intro = "1.10";
[Tooltip("第三轮成功语音")]
public string audioRound3Success = "1.11";
[Tooltip("失败提示语音")]
public string audioFail = "1.6";
[Tooltip("成功音效")]
public string audioSuccessEffect = "2.2";
[Header("小精灵介绍文本")]
[Tooltip("第一轮介绍文本")]
public string introTextRound1 = "在这一轮中,你需要找出表情最邪恶的雕像!仔细观察它们的面部表情,找出那个带着邪恶笑容的雕像!";
[Tooltip("第二轮介绍文本")]
public string introTextRound2 = "第二轮来了!注意观察雕像的双手姿势,找到那个带着恶毒双手的雕像!";
[Tooltip("第三轮介绍文本")]
public string introTextRound3 = "最后一轮!找到那个代表嫉妒之心的雕像!只有打破它才能通关!";
public GameObject crystalEnvironment;
#endregion
#region
public enum EventState
{
None, // 未开始
Round1_Intro, // 第一轮介绍
Round1_Playing, // 第一轮进行中
Round1_Success, // 第一轮成功
Round1_Fail, // 第一轮失败
Round2_Intro, // 第二轮介绍
Round2_Playing, // 第二轮进行中
Round2_Success, // 第二轮成功
Round2_Fail, // 第二轮失败
Round3_Intro, // 第三轮介绍
Round3_Playing, // 第三轮进行中
Round3_Success, // 第三轮成功
Round3_Fail, // 第三轮失败
TimeUp, // 超时
Complete // 完成
}
public EventState currentState { get; private set; } = EventState.None;
public int currentRound { get; private set; } = 0;
#endregion
protected override void Start()
{
base.Start();
if (crystalEnvironment != null)
crystalEnvironment.SetActive(false);
round1Statues.gameObject.SetActive(false);
round2Statues.gameObject.SetActive(false);
round3Statues.gameObject.SetActive(false);
}
#region
private CrystalStatueGroup currentGroup;
private float currentRoundTime;
private bool isRoundActive;
private bool isTimeUp;
private Coroutine roundTimerCoroutine;
#endregion
#region
public override void StartCutscene()
{
if (isPlaying)
{
Debug.LogWarning("[CrystalStatueEvent] 事件已在进行中");
return;
}
isPlaying = true;
currentStep = 0;
currentRound = 0;
currentState = EventState.None;
if (debugMode)
Debug.Log("[CrystalStatueEvent] ========== 开始水晶雕像事件 ==========");
// 进入雕像事件模式
if (GameManager.Ins.playerRightHand != null)
{
GameManager.Ins.playerRightHand.EnterStatueEventMode();
}
StartCoroutine(PlayEventSequence());
}
public override string GetCurrentStateDescription()
{
return $"轮次{currentRound}: {currentState}, 剩余时间: {currentRoundTime:F1}秒";
}
#endregion
#region
private IEnumerator PlayEventSequence()
{
// 等待小精灵初始化完成
yield return StartCoroutine(WaitForSpiritInitialized());
crystalEnvironment.SetActive(true);
// ========== 第一轮:邪恶表情 ==========
currentRound = 1;
yield return StartCoroutine(PlayRound(round1Statues, audioRound1Intro, audioRound1Success));
if (!isPlaying) yield break;
// ========== 第二轮:恶毒双手 ==========
currentRound = 2;
yield return new WaitForSeconds(delayBetweenRounds);
yield return StartCoroutine(PlayRound(round2Statues, audioRound2Intro, audioRound2Success));
if (!isPlaying) yield break;
// ========== 第三轮:嫉妒之心 ==========
currentRound = 3;
yield return new WaitForSeconds(delayBetweenRounds);
yield return StartCoroutine(PlayRound(round3Statues, audioRound3Intro, audioRound3Success));
if (!isPlaying) yield break;
// ========== 完成 ==========
currentState = EventState.Complete;
currentStep = 10;
if (debugMode)
Debug.Log("[CrystalStatueEvent] ========== 水晶雕像事件完成 ==========");
crystalEnvironment.SetActive(false);
Cleanup();
TriggerComplete();
}
protected override void TriggerComplete()
{
base.TriggerComplete();
GameLocal.Ins.ShowGoEffect(5,2, () =>
{
GameManager.Ins.StartCutscene(2);
});
}
/// <summary>
/// 执行一轮挑战
/// </summary>
private IEnumerator PlayRound(CrystalStatueGroup group, string introAudio, string successAudio)
{
if (group == null)
{
Debug.LogError($"[CrystalStatueEvent] 第{currentRound}轮雕像组未配置!");
yield break;
}
currentGroup = group;
// 设置状态
SetRoundState(EventState.Round1_Intro, EventState.Round2_Intro, EventState.Round3_Intro);
// ====== 第一步:先激活场景环境(但不激活雕像)======
crystalEnvironment.SetActive(true);
// ====== 第二步:小精灵返回跟随位置 ======
yield return StartCoroutine(SpiritReturnToFollowRoutine());
// ====== 第三步:小精灵介绍(文字+语音)======
yield return StartCoroutine(SpiritIntroRoutineWithAudio(introAudio));
// ====== 第四步:激活水晶雕像(可以开始敲击)======
group.ActivateGroup();
group.EnableHit();
group.SetCanShowCrack(true);
if (debugMode)
Debug.Log($"[CrystalStatueEvent] 第{currentRound}轮开始 - 雕像已激活");
// 开始游戏
SetRoundState(EventState.Round1_Playing, EventState.Round2_Playing, EventState.Round3_Playing);
// 启动计时器
isRoundActive = true;
isTimeUp = false;
currentRoundTime = roundTimeLimit;
roundTimerCoroutine = StartCoroutine(RoundTimer());
// 等待结果
bool roundComplete = false;
bool roundSuccess = false;
System.Action<CrystalStatueGroup> onTargetBroken = (g) =>
{
roundComplete = true;
roundSuccess = true;
};
System.Action<CrystalStatueGroup> onWrongBroken = (g) =>
{
roundComplete = true;
roundSuccess = false;
};
group.OnTargetBroken = onTargetBroken;
group.OnWrongStatueBroken = onWrongBroken;
// 等待玩家操作或超时
yield return new WaitUntil(() => roundComplete || isTimeUp);
// 停止计时器
if (roundTimerCoroutine != null)
{
StopCoroutine(roundTimerCoroutine);
roundTimerCoroutine = null;
}
isRoundActive = false;
// 处理结果
if (isTimeUp)
{
// 超时失败
currentState = EventState.TimeUp;
yield return StartCoroutine(HandleTimeUp(group));
yield return StartCoroutine(WaitForAudioComplete(audioFail, 10f));
}
else if (roundSuccess)
{
// 成功 - 小精灵跳舞
SetRoundState(EventState.Round1_Success, EventState.Round2_Success, EventState.Round3_Success);
// 让其他雕像消失
group.DisappearAllStatues(0.5f);
// 播放成功音效和语音
PlayAudio2D(audioSuccessEffect);
PlayAudio2D(successAudio);
// 小精灵跳舞
yield return StartCoroutine(SpiritDanceRoutine());
if (debugMode)
Debug.Log($"[CrystalStatueEvent] 第{currentRound}轮成功!");
yield return StartCoroutine(WaitForAudioComplete(successAudio, 10f));
}
else
{
// 失败(击打错误)- 小精灵沮丧
SetRoundState(EventState.Round1_Fail, EventState.Round2_Fail, EventState.Round3_Fail);
yield return StartCoroutine(HandleFail(group));
// 小精灵沮丧
yield return StartCoroutine(SpiritSadRoutine());
yield return StartCoroutine(WaitForAudioComplete(audioFail, 10f));
}
// 等待下沉动画完成后隐藏(下沉动画是异步的)
yield return StartCoroutine(WaitForGroupDeactivate(group));
currentGroup = null;
}
/// <summary>
/// 等待雕像组下沉完成
/// </summary>
private IEnumerator WaitForGroupDeactivate(CrystalStatueGroup group)
{
// 触发下沉动画
group.DeactivateGroup();
// 等待下沉动画完成(下沉时长 + 错开时间)
float maxWaitTime = group.sinkDuration + group.staggerDelay * 5 + 0.5f;
yield return new WaitForSeconds(maxWaitTime);
}
/// <summary>
/// 设置轮次状态
/// </summary>
private void SetRoundState(EventState state1, EventState state2, EventState state3)
{
switch (currentRound)
{
case 1: currentState = state1; break;
case 2: currentState = state2; break;
case 3: currentState = state3; break;
}
}
#endregion
#region
private IEnumerator RoundTimer()
{
while (isRoundActive && currentRoundTime > 0)
{
currentRoundTime -= Time.deltaTime;
UpdateTimerUI(currentRoundTime);
yield return null;
}
if (currentRoundTime <= 0)
{
isTimeUp = true;
if (debugMode)
Debug.Log("[CrystalStatueEvent] 时间耗尽!");
}
}
/// <summary>
/// 更新计时器UI子类可重写
/// </summary>
protected virtual void UpdateTimerUI(float remainingTime)
{
// TODO: 更新HUD倒计时显示
}
/// <summary>
/// 等待音频播放完成
/// </summary>
private IEnumerator WaitForAudioComplete(string audioName, float maxWaitTime = 10f)
{
float elapsedTime = 0f;
yield return new WaitForSeconds(0.1f);
while (elapsedTime < maxWaitTime)
{
MasterAudioGroup audioGroup = MasterAudio.GrabGroup(audioName);
if (audioGroup == null || audioGroup.ActiveVoices <= 0)
{
yield break;
}
elapsedTime += Time.deltaTime;
yield return null;
}
}
#endregion
#region
/// <summary>
/// 处理超时
/// </summary>
private IEnumerator HandleTimeUp(CrystalStatueGroup group)
{
if (debugMode)
Debug.Log("[CrystalStatueEvent] 处理超时失败");
// 让雕像消失
group.DisappearAllStatues(0.5f);
// 播放失败语音
PlayAudio2D(audioFail);
yield return new WaitForSeconds(0.5f);
}
/// <summary>
/// 处理失败(击打错误)
/// </summary>
private IEnumerator HandleFail(CrystalStatueGroup group)
{
if (debugMode)
Debug.Log("[CrystalStatueEvent] 处理失败(击打错误)");
// 让所有雕像狞笑
group.PlayAllEvilLaugh();
yield return new WaitForSeconds(1f);
// 让所有雕像消失
group.DisappearAllStatues(0.5f);
// 播放失败语音
PlayAudio2D(audioFail);
yield return new WaitForSeconds(0.5f);
}
#endregion
#region
private void PlayAudio2D(string audioName)
{
if (!string.IsNullOrEmpty(audioName))
{
MasterAudio.PlaySound(audioName);
}
}
#endregion
#region
/// <summary>
/// 等待小精灵初始化完成
/// </summary>
private IEnumerator WaitForSpiritInitialized()
{
// 等待小精灵实例创建
yield return new WaitUntil(() => SpiritController.Instance != null);
var spirit = SpiritController.Instance;
// 等待小精灵初始化完成isInitialized 变为 true
float waitTime = 0f;
float maxWaitTime = 5f;
while (!spirit.IsInitialized && waitTime < maxWaitTime)
{
yield return null;
waitTime += Time.deltaTime;
}
if (waitTime >= maxWaitTime)
{
Debug.LogWarning("[CrystalStatueEvent] 小精灵初始化超时,继续执行...");
}
else
{
if (debugMode)
Debug.Log($"[CrystalStatueEvent] 小精灵初始化完成,耗时 {waitTime:F2}秒");
}
// 额外等待一小段时间确保 playerHead 已正确设置
yield return new WaitForSeconds(0.2f);
}
/// <summary>
/// 小精灵返回跟随位置协程
/// </summary>
private IEnumerator SpiritReturnToFollowRoutine()
{
var spirit = SpiritController.Instance;
if (spirit == null)
{
Debug.LogWarning("[CrystalStatueEvent] 未找到小精灵控制器");
yield break;
}
if (debugMode)
Debug.Log("[CrystalStatueEvent] 小精灵返回跟随位置");
// 触发小精灵返回跟随位置
spirit.ReturnToFollow();
// 等待一小段时间让小精灵开始返回
yield return new WaitForSeconds(0.3f);
}
/// <summary>
/// 小精灵介绍协程 - 飞到正前方介绍后飞回跟随位置
/// </summary>
private IEnumerator SpiritIntroRoutine()
{
var spirit = SpiritController.Instance;
if (spirit == null)
{
Debug.LogWarning("[CrystalStatueEvent] 未找到小精灵控制器");
yield break;
}
// 根据当前轮数获取介绍文本
string introText = currentRound switch
{
1 => introTextRound1,
2 => introTextRound2,
3 => introTextRound3,
_ => introTextRound1
};
if (debugMode)
Debug.Log($"[CrystalStatueEvent] 小精灵介绍: {introText}");
// 触发小精灵介绍(小精灵会飞正前方 → 介绍 → 飞回跟随位置)
spirit.StartIntroduction(introText);
// 等待介绍完成
yield return spirit.WaitForIntroComplete();
if (debugMode)
Debug.Log("[CrystalStatueEvent] 小精灵介绍完成");
}
/// <summary>
/// 小精灵介绍协程(带语音)- 飞到正前方 → 显示文字+播放语音 → 飞回跟随位置
/// </summary>
private IEnumerator SpiritIntroRoutineWithAudio(string audioName)
{
var spirit = SpiritController.Instance;
if (spirit == null)
{
Debug.LogWarning("[CrystalStatueEvent] 未找到小精灵控制器");
yield break;
}
// 根据当前轮数获取介绍文本
string introText = currentRound switch
{
1 => introTextRound1,
2 => introTextRound2,
3 => introTextRound3,
_ => introTextRound1
};
Debug.Log($"[CrystalStatueEvent] ========== 小精灵开始介绍 ==========");
Debug.Log($"[CrystalStatueEvent] 介绍文本: {introText}");
Debug.Log($"[CrystalStatueEvent] 语音: {audioName}");
// 设置为介绍状态(会飞到正前方)
spirit.SetState(SpiritState.Introduce);
Debug.Log("[CrystalStatueEvent] 状态设为Introduce开始飞向玩家...");
// 飞到正前方
yield return spirit.FlyToIntroPosition();
Debug.Log("[SpiritController] 飞行到介绍位置完成显示UI...");
// 显示UI文字
spirit.ShowIntroUI(introText);
Debug.Log("[CrystalStatueEvent] 开始播放语音...");
// 播放介绍语音
if (!string.IsNullOrEmpty(audioName))
{
PlayAudio2D(audioName);
}
// 等待语音播放完成
yield return StartCoroutine(WaitForAudioComplete(audioName, 15f));
Debug.Log("[CrystalStatueEvent] 语音播放完成隐藏UI...");
// 等待文字显示一小段时间
yield return new WaitForSeconds(0.5f);
// 隐藏UI
spirit.HideIntroUI();
Debug.Log("[CrystalStatueEvent] 设置为返回状态...");
// 设置为返回状态(飞回跟随位置)
spirit.SetState(SpiritState.Return);
// 等待返回完成
yield return new WaitForSeconds(1f);
Debug.Log("[CrystalStatueEvent] 恢复跟随状态...");
// 恢复跟随状态
spirit.SetState(SpiritState.Follow);
Debug.Log("[CrystalStatueEvent] ========== 小精灵介绍完成 ==========");
}
/// <summary>
/// 小精灵跳舞协程 - 在正前方跳舞
/// </summary>
private IEnumerator SpiritDanceRoutine()
{
var spirit = SpiritController.Instance;
if (spirit == null)
{
Debug.LogWarning("[CrystalStatueEvent] 未找到小精灵控制器");
yield break;
}
if (debugMode)
Debug.Log("[CrystalStatueEvent] 小精灵跳舞");
// 触发小精灵跳舞
spirit.PlayDance();
// 跳舞会持续到语音结束,这里不需要等待,因为外部已经在等待语音了
// 如果需要等待跳舞完成,可以添加额外逻辑
yield return null;
}
/// <summary>
/// 小精灵沮丧协程 - 在正前方沮丧
/// </summary>
private IEnumerator SpiritSadRoutine()
{
var spirit = SpiritController.Instance;
if (spirit == null)
{
Debug.LogWarning("[CrystalStatueEvent] 未找到小精灵控制器");
yield break;
}
if (debugMode)
Debug.Log("[CrystalStatueEvent] 小精灵沮丧");
// 触发小精灵沮丧
spirit.PlaySad();
// 沮丧效果会持续,外部需要在适当时候调用 ReturnToFollow
yield return null;
}
#endregion
#region
public override void Cleanup()
{
// 停止计时器
if (roundTimerCoroutine != null)
{
StopCoroutine(roundTimerCoroutine);
roundTimerCoroutine = null;
}
isRoundActive = false;
// 隐藏所有雕像组
if (round1Statues != null) round1Statues.DeactivateGroup();
if (round2Statues != null) round2Statues.DeactivateGroup();
if (round3Statues != null) round3Statues.DeactivateGroup();
currentGroup = null;
// 退出雕像事件模式
if (GameManager.Ins.playerRightHand != null)
{
GameManager.Ins.playerRightHand.ExitStatueEventMode();
}
base.Cleanup();
}
#endregion
#region
#if UNITY_EDITOR
[ContextMenu("开始事件")]
private void TestStartEvent()
{
StartCutscene();
}
[ContextMenu("停止事件")]
private void TestStopEvent()
{
StopCutscene();
}
[ContextMenu("清理")]
private void TestCleanup()
{
Cleanup();
}
#endif
#endregion
}