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

682 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 System.Collections.Generic;
using DarkTonic.MasterAudio;
using DG.Tweening;
/// <summary>
/// 水晶雕像事件 - 继承CutsceneBase
/// 管理三轮水晶雕像挑战的完整流程
/// </summary>
public class CrystalStatueEvent : CutsceneBase
{
#region
[Header("雕像预制体")]
public GameObject crystalStatuePrefab;
[Header("生成配置")]
public int statueCount = 5;
public float statueRadius = 3f; // 五角星排列半径
public float statueHeight = 0f; // 雕像高度
[Header("时间配置")]
public float roundTimeLimit = 90f; // 每轮时间限制(秒)
public float delayBetweenRounds = 3f; // 轮次间隔
[Header("音效配置")]
public string audioRound1Intro = "1.5"; // 第一轮介绍
public string audioRound1Success = "1.7"; // 第一轮成功
public string audioRound2Intro = "1.8"; // 第二轮介绍
public string audioRound2Success = "1.9"; // 第二轮成功
public string audioRound3Intro = "1.10"; // 第三轮介绍
public string audioRound3Success = "1.11"; // 第三轮成功
public string audioFail = "1.6"; // 失败提示
public string audioCrystalBreak = "2.1"; // 水晶敲碎
public string audioSuccessEffect = "2.2"; // 成功音效
public string audioEvilLaugh = "2.3"; // 狞笑音效
[Header("场景配置")]
public GameObject crystalWorldEnvironment; // 水晶世界环境
#endregion
#region
public enum CrystalEventState
{
None,
SceneTransition, // 场景切换
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 CrystalEventState currentState = CrystalEventState.None;
public int currentRound = 0;
#endregion
#region
private List<CrystalStatue> spawnedStatues = new List<CrystalStatue>();
private CrystalStatue targetStatue; // 当前目标雕像
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 = CrystalEventState.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()
{
// ========== 场景切换 ==========
currentState = CrystalEventState.SceneTransition;
currentStep = 1;
if (debugMode)
Debug.Log("[CrystalStatueEvent] 步骤1: 场景切换到水晶世界");
// 激活水晶世界环境
if (crystalWorldEnvironment != null)
crystalWorldEnvironment.SetActive(true);
// 播放BGM
GameManager.Ins.PlayBGM(1);
yield return new WaitForSeconds(1f);
// ========== 第一轮:邪恶表情 ==========
currentRound = 1;
yield return StartCoroutine(PlayRound(1, CrystalStatue.StatueType.EvilFace,
audioRound1Intro, audioRound1Success));
if (!isPlaying) yield break;
// ========== 第二轮:恶毒双手 ==========
currentRound = 2;
yield return new WaitForSeconds(delayBetweenRounds);
yield return StartCoroutine(PlayRound(2, CrystalStatue.StatueType.EvilHands,
audioRound2Intro, audioRound2Success));
if (!isPlaying) yield break;
// ========== 第三轮:嫉妒之心 ==========
currentRound = 3;
yield return new WaitForSeconds(delayBetweenRounds);
yield return StartCoroutine(PlayRound(3, CrystalStatue.StatueType.JealousHeart,
audioRound3Intro, audioRound3Success));
if (!isPlaying) yield break;
// ========== 完成 ==========
currentState = CrystalEventState.Complete;
currentStep = 10;
if (debugMode)
Debug.Log("[CrystalStatueEvent] ========== 水晶雕像事件完成 ==========");
// 清理雕像
CleanupStatues();
TriggerComplete();
}
/// <summary>
/// 执行一轮挑战
/// </summary>
private IEnumerator PlayRound(int round, CrystalStatue.StatueType type,
string introAudio, string successAudio)
{
// 设置状态
SetRoundState(round, CrystalEventState.Round1_Intro, CrystalEventState.Round2_Intro, CrystalEventState.Round3_Intro);
// 生成雕像
SpawnStatuesForRound(round, type);
// 播放介绍语音
PlayAudio2D(introAudio);
if (debugMode)
Debug.Log($"[CrystalStatueEvent] 第{round}轮开始: {type}");
// 等待介绍语音播放完成
yield return StartCoroutine(WaitForAudioComplete(introAudio, 10f));
// 额外等待一小段时间
yield return new WaitForSeconds(0.5f);
// 开始游戏
SetRoundState(round, CrystalEventState.Round1_Playing, CrystalEventState.Round2_Playing, CrystalEventState.Round3_Playing);
// 启动计时器
isRoundActive = true;
isTimeUp = false;
currentRoundTime = roundTimeLimit;
roundTimerCoroutine = StartCoroutine(RoundTimer());
// 等待结果
bool roundComplete = false;
bool roundSuccess = false;
System.Action<bool> onRoundEnd = (success) =>
{
roundComplete = true;
roundSuccess = success;
};
// 注册雕像碎裂回调
foreach (var statue in spawnedStatues)
{
statue.OnStatueBroken = (s, isTarget) =>
{
if (isTarget)
{
// 击中目标,成功
onRoundEnd(true);
}
else
{
// 击中非目标,失败
onRoundEnd(false);
}
};
}
// 等待玩家操作
yield return new WaitUntil(() => roundComplete || isTimeUp);
// 停止计时器
if (roundTimerCoroutine != null)
{
StopCoroutine(roundTimerCoroutine);
roundTimerCoroutine = null;
}
isRoundActive = false;
// 处理结果
// 第三轮特殊处理:无论成功失败都结束事件
if (round == 3)
{
if (isTimeUp)
{
// 第三轮超时失败
currentState = CrystalEventState.TimeUp;
yield return StartCoroutine(HandleTimeUp());
yield return StartCoroutine(WaitForAudioComplete(audioFail, 10f));
if (debugMode)
Debug.Log("[CrystalStatueEvent] 第三轮超时失败,结束水晶雕像事件");
}
else if (roundSuccess)
{
// 第三轮成功
currentState = CrystalEventState.Round3_Success;
yield return StartCoroutine(AllStatuesDisappearExcept(null));
PlayAudio2D(successAudio);
PlayAudio2D(audioSuccessEffect);
if (debugMode)
Debug.Log("[CrystalStatueEvent] 第三轮成功! 播放1.11语音");
yield return StartCoroutine(WaitForAudioComplete(successAudio, 10f));
}
else
{
// 第三轮失败(击打错误)
currentState = CrystalEventState.Round3_Fail;
yield return StartCoroutine(HandleFailButContinue());
yield return StartCoroutine(WaitForAudioComplete(audioFail, 10f));
if (debugMode)
Debug.Log("[CrystalStatueEvent] 第三轮失败播放1.6语音,结束水晶雕像事件");
}
// 第三轮结束,清理雕像并完成事件
CleanupStatues();
currentState = CrystalEventState.Complete;
currentStep = 10;
if (debugMode)
Debug.Log("[CrystalStatueEvent] ========== 水晶雕像事件完成 ==========");
TriggerComplete();
yield break; // 直接结束,不继续后续流程
}
// 第一轮和第二轮的处理
if (isTimeUp)
{
// 超时失败
currentState = CrystalEventState.TimeUp;
yield return StartCoroutine(HandleTimeUp());
// 等待失败语音播放完成后再进入下一轮
yield return StartCoroutine(WaitForAudioComplete(audioFail, 10f));
yield return new WaitForSeconds(0.5f);
if (debugMode)
Debug.Log($"[CrystalStatueEvent] 第{round}轮超时,进入下一轮");
}
else if (roundSuccess)
{
// 成功
SetRoundState(round, CrystalEventState.Round1_Success, CrystalEventState.Round2_Success, CrystalEventState.Round3_Success);
// 所有其他雕像消失
yield return StartCoroutine(AllStatuesDisappearExcept(null));
// 播放成功语音
PlayAudio2D(successAudio);
PlayAudio2D(audioSuccessEffect);
if (debugMode)
Debug.Log($"[CrystalStatueEvent] 第{round}轮成功!");
// 等待成功语音播放完成后再进入下一轮
yield return StartCoroutine(WaitForAudioComplete(successAudio, 10f));
yield return new WaitForSeconds(0.5f);
}
else
{
// 失败(击打错误)- 播放失败提示,但仍进入下一轮
SetRoundState(round, CrystalEventState.Round1_Fail, CrystalEventState.Round2_Fail, CrystalEventState.Round3_Fail);
yield return StartCoroutine(HandleFailButContinue());
// 等待失败语音播放完成后再进入下一轮
yield return StartCoroutine(WaitForAudioComplete(audioFail, 10f));
yield return new WaitForSeconds(0.5f);
if (debugMode)
Debug.Log($"[CrystalStatueEvent] 第{round}轮失败(击打错误),进入下一轮");
}
// 清理雕像
CleanupStatues();
}
/// <summary>
/// 设置轮次状态
/// </summary>
private void SetRoundState(int round, CrystalEventState state1, CrystalEventState state2, CrystalEventState state3)
{
switch (round)
{
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;
// 更新UI如果有
UpdateTimerUI(currentRoundTime);
yield return null;
}
if (currentRoundTime <= 0)
{
isTimeUp = true;
if (debugMode)
Debug.Log("[CrystalStatueEvent] 时间耗尽!");
}
}
/// <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)
{
// 音频播放完成
if (debugMode)
Debug.Log($"[CrystalStatueEvent] 音频 {audioName} 播放完成,耗时: {elapsedTime:F1}秒");
yield break;
}
elapsedTime += Time.deltaTime;
yield return null;
}
if (debugMode)
Debug.Log($"[CrystalStatueEvent] 音频 {audioName} 等待超时");
}
/// <summary>
/// 更新计时器UI子类可重写
/// </summary>
protected virtual void UpdateTimerUI(float remainingTime)
{
// TODO: 更新HUD倒计时显示
// HUDPanel.Ins.UpdateTimer(remainingTime);
}
#endregion
#region
/// <summary>
/// 为指定轮次生成雕像
/// </summary>
private void SpawnStatuesForRound(int round, CrystalStatue.StatueType type)
{
CleanupStatues();
// 生成五角星排列的位置
Vector3[] positions = GeneratePentagramPositions(GameLocal.Ins.self.transform.position, statueRadius);
// 随机选择目标位置
int targetIndex = Random.Range(0, statueCount);
for (int i = 0; i < statueCount; i++)
{
Vector3 pos = positions[i];
pos.y = statueHeight;
GameObject statueObj = Instantiate(crystalStatuePrefab, pos, Quaternion.identity);
statueObj.name = $"CrystalStatue_{i}";
// 面向中心
statueObj.transform.LookAt(GameLocal.Ins.self.transform.position);
statueObj.transform.eulerAngles = new Vector3(0, statueObj.transform.eulerAngles.y, 0);
CrystalStatue statue = statueObj.GetComponent<CrystalStatue>();
if (statue == null)
{
statue = statueObj.AddComponent<CrystalStatue>();
}
// 初始化雕像
bool isTarget = (i == targetIndex);
statue.Initialize(type, isTarget);
if (isTarget)
{
targetStatue = statue;
if (debugMode)
Debug.Log($"[CrystalStatueEvent] 目标雕像: 位置{i}");
}
spawnedStatues.Add(statue);
}
if (debugMode)
Debug.Log($"[CrystalStatueEvent] 生成{statueCount}个雕像, 类型: {type}");
}
/// <summary>
/// 生成五角星排列位置
/// </summary>
private Vector3[] GeneratePentagramPositions(Vector3 center, float radius)
{
Vector3[] positions = new Vector3[statueCount];
for (int i = 0; i < statueCount; i++)
{
// 五角星角度,从顶部开始
float angle = i * (360f / statueCount) * Mathf.Deg2Rad - 90f * Mathf.Deg2Rad;
positions[i] = center + new Vector3(
Mathf.Cos(angle) * radius,
0,
Mathf.Sin(angle) * radius
);
}
return positions;
}
#endregion
#region
/// <summary>
/// 处理超时
/// </summary>
private IEnumerator HandleTimeUp()
{
if (debugMode)
Debug.Log("[CrystalStatueEvent] 处理超时失败");
// 所有雕像消失
yield return StartCoroutine(AllStatuesDisappear());
// 播放失败语音
PlayAudio2D(audioFail);
}
/// <summary>
/// 处理失败
/// </summary>
private IEnumerator HandleFail()
{
if (debugMode)
Debug.Log("[CrystalStatueEvent] 处理失败");
// 所有雕像狞笑
foreach (var statue in spawnedStatues)
{
if (!statue.IsBroken())
{
statue.PlayEvilLaugh();
}
}
// 播放狞笑音效
PlayAudio2D(audioEvilLaugh);
yield return new WaitForSeconds(1f);
// 所有雕像消失
yield return StartCoroutine(AllStatuesDisappear());
// 播放失败语音
PlayAudio2D(audioFail);
}
/// <summary>
/// 处理失败但继续进入下一轮
/// </summary>
private IEnumerator HandleFailButContinue()
{
if (debugMode)
Debug.Log("[CrystalStatueEvent] 处理失败(继续下一轮)");
// 所有雕像狞笑
foreach (var statue in spawnedStatues)
{
if (!statue.IsBroken())
{
statue.PlayEvilLaugh();
}
}
// 播放狞笑音效
PlayAudio2D(audioEvilLaugh);
yield return new WaitForSeconds(1f);
// 所有雕像消失
yield return StartCoroutine(AllStatuesDisappear());
// 播放失败提示语音
PlayAudio2D(audioFail);
}
/// <summary>
/// 所有雕像消失
/// </summary>
private IEnumerator AllStatuesDisappear()
{
foreach (var statue in spawnedStatues)
{
if (!statue.IsBroken())
{
statue.ForceDisappear(0.5f);
}
}
yield return new WaitForSeconds(0.6f);
}
/// <summary>
/// 除指定雕像外所有雕像消失
/// </summary>
private IEnumerator AllStatuesDisappearExcept(CrystalStatue exception)
{
foreach (var statue in spawnedStatues)
{
if (statue != exception && !statue.IsBroken())
{
statue.ForceDisappear(0.5f);
}
}
yield return new WaitForSeconds(0.6f);
}
#endregion
#region
/// <summary>
/// 清理所有雕像
/// </summary>
private void CleanupStatues()
{
foreach (var statue in spawnedStatues)
{
if (statue != null)
{
Destroy(statue.gameObject);
}
}
spawnedStatues.Clear();
targetStatue = null;
}
public override void Cleanup()
{
CleanupStatues();
if (crystalWorldEnvironment != null)
crystalWorldEnvironment.SetActive(false);
// 退出雕像事件模式,恢复道具
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()
{
CleanupStatues();
}
[ContextMenu("测试:生成第一轮雕像")]
private void TestSpawnRound1()
{
SpawnStatuesForRound(1, CrystalStatue.StatueType.EvilFace);
}
[ContextMenu("测试:生成第二轮雕像")]
private void TestSpawnRound2()
{
SpawnStatuesForRound(2, CrystalStatue.StatueType.EvilHands);
}
[ContextMenu("测试:生成第三轮雕像")]
private void TestSpawnRound3()
{
SpawnStatuesForRound(3, CrystalStatue.StatueType.JealousHeart);
}
#endif
#endregion
}