682 lines
21 KiB
C#
682 lines
21 KiB
C#
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
|
||
} |