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

538 lines
14 KiB
C#

using UnityEngine;
using DarkTonic.MasterAudio;
using DG.Tweening;
using System;
/// <summary>
/// 水晶雕像控制器 - 单个雕像的控制逻辑
/// 包含血量、裂痕效果、击打反馈等功能
/// </summary>
public class CrystalStatue : MonoBehaviour
{
#region
[Header("雕像类型")]
public StatueType statueType;
public bool isTarget; // 是否是目标雕像
[Header("血量配置")]
public int maxHealth = 3;
private int currentHealth;
[Header("裂痕效果")]
public Renderer statueRenderer;
public Material[] crackMaterials; // 0=正常, 1=裂痕1, 2=裂痕2, 3=碎裂前
public GameObject[] crackDecals; // 裂痕贴花对象
[Header("特效")]
public ParticleSystem hitParticle; // 击打粒子效果
public ParticleSystem breakParticle; // 碎裂粒子效果
public ParticleSystem evilLaughParticle; // 狞笑粒子效果(失败时)
[Header("音效")]
public string hitSound = "2.1"; // 击打音效
public string crackSound = "2.1"; // 裂痕音效
public string breakSound = "2.2"; // 碎裂音效
public string evilLaughSound = "2.3"; // 狞笑音效
[Header("动画")]
public Animator animator;
public string hitAnimTrigger = "Hit";
public string breakAnimTrigger = "Break";
public string evilLaughAnimTrigger = "EvilLaugh";
[Header("视觉效果")]
public GameObject heartObject; // 心脏显示对象(第三轮用)
public GameObject handsObject; // 双手显示对象(第二轮用)
public ParticleSystem specialEffect; // 特殊效果(心脏跳动/手部特效)
[Header("表情区分(第一轮用)")]
public GameObject evilFaceObject; // 邪恶表情对象
[Header("碎裂配置")]
public float breakDuration = 1f;
public float disappearDelay = 0.5f;
[Header("震动配置")]
public float hitShakeDuration = 0.2f; // 击打震动持续时间
public float hitShakeStrength = 0.1f; // 击打震动强度
public int hitShakeVibrato = 10; // 震动频率
public float hitShakeRandomness = 90f; // 震动随机性
[Header("调试")]
public bool debugMode = true;
#endregion
#region
public enum StatueType
{
Normal, // 普通雕像
EvilFace, // 邪恶表情
EvilHands, // 恶毒双手
JealousHeart // 嫉妒之心
}
public enum StatueState
{
Idle,
Hit,
Breaking,
Broken,
EvilLaugh
}
public StatueState currentState = StatueState.Idle;
// 当前裂痕等级
private int crackLevel = 0;
// 是否可以被击打
private bool canHit = true;
// 事件回调
public Action<CrystalStatue, bool> OnStatueBroken; // 雕像碎裂回调(雕像, 是否是目标)
#endregion
#region Unity生命周期
private void Awake()
{
if (animator == null)
animator = GetComponent<Animator>();
if (statueRenderer == null)
statueRenderer = GetComponentInChildren<Renderer>();
}
private void Start()
{
currentHealth = maxHealth;
crackLevel = 0;
canHit = true;
currentState = StatueState.Idle;
}
#endregion
#region
/// <summary>
/// 初始化雕像
/// </summary>
public void Initialize(StatueType type, bool isTarget)
{
this.statueType = type;
this.isTarget = isTarget;
currentHealth = maxHealth;
crackLevel = 0;
canHit = true;
currentState = StatueState.Idle;
// 根据类型设置视觉效果
SetupVisualsByType();
if (debugMode)
Debug.Log($"[CrystalStatue] 初始化雕像: {type}, 是否目标: {isTarget}");
}
/// <summary>
/// 根据类型设置视觉效果
/// </summary>
private void SetupVisualsByType()
{
// 隐藏所有特殊部件
if (heartObject != null)
heartObject.SetActive(false);
if (handsObject != null)
handsObject.SetActive(false);
if (evilFaceObject != null)
evilFaceObject.SetActive(false);
if (specialEffect != null)
specialEffect.Stop();
if (isTarget)
{
// 根据类型显示对应部件
switch (statueType)
{
case StatueType.EvilFace:
// 第一轮:邪恶表情区分
// 目标雕像显示邪恶表情,非目标显示和善表情
if (isTarget)
{
// 邪恶表情
if (evilFaceObject != null)
evilFaceObject.SetActive(true);
if (debugMode)
Debug.Log("[CrystalStatue] 设置邪恶表情(目标雕像)");
}
else
{
if (debugMode)
Debug.Log("[CrystalStatue] 设置和善表情(非目标雕像)");
}
break;
case StatueType.EvilHands:
// 第二轮:恶毒双手区分
if (handsObject != null)
handsObject.SetActive(true);
if (specialEffect != null && isTarget)
specialEffect.Play();
break;
case StatueType.JealousHeart:
// 第三轮:嫉妒之心区分
if (heartObject != null)
heartObject.SetActive(true);
if (specialEffect != null && isTarget)
specialEffect.Play();
break;
}
}
// 重置裂痕
UpdateCrackVisual(0);
}
#endregion
#region
/// <summary>
/// 玩家击中雕像
/// </summary>
public void OnPlayerHit()
{
if (!canHit || currentState == StatueState.Broken)
{
if (debugMode)
Debug.Log("[CrystalStatue] 雕像不可被击打");
return;
}
currentState = StatueState.Hit;
currentHealth--;
crackLevel = maxHealth - currentHealth;
if (debugMode)
Debug.Log($"[CrystalStatue] 雕像被击打! 剩余血量: {currentHealth}, 裂痕等级: {crackLevel}");
// 播放击打音效
PlayHitSound();
// 播放击打特效
PlayHitEffect();
// 播放击打动画
PlayHitAnimation();
// 更新裂痕视觉效果
UpdateCrackVisual(crackLevel);
// 检查是否碎裂
if (currentHealth <= 0)
{
Break();
}
else
{
// 短暂冷却
canHit = false;
DOVirtual.DelayedCall(0.2f, () =>
{
canHit = true;
if (currentHealth > 0)
currentState = StatueState.Idle;
});
}
}
/// <summary>
/// 雕像碎裂
/// </summary>
public void Break()
{
currentState = StatueState.Breaking;
canHit = false;
if (debugMode)
Debug.Log($"[CrystalStatue] 雕像碎裂! 是否目标: {isTarget}");
// 播放碎裂音效
PlayBreakSound();
// 播放碎裂特效
PlayBreakEffect();
// 播放碎裂动画
PlayBreakAnimation();
// 延迟后触发回调
DOVirtual.DelayedCall(disappearDelay, () =>
{
currentState = StatueState.Broken;
OnStatueBroken?.Invoke(this, isTarget);
});
}
/// <summary>
/// 播放邪恶狞笑(失败时)
/// </summary>
public void PlayEvilLaugh()
{
currentState = StatueState.EvilLaugh;
if (debugMode)
Debug.Log("[CrystalStatue] 播放邪恶狞笑");
// 播放狞笑音效
PlayEvilLaughSound();
// 播放狞笑特效
if (evilLaughParticle != null)
evilLaughParticle.Play();
// 播放狞笑动画
if (animator != null)
animator.SetTrigger(evilLaughAnimTrigger);
}
/// <summary>
/// 强制消失(其他雕像)
/// </summary>
public void ForceDisappear(float duration = 0.5f)
{
canHit = false;
// 缩放消失效果
transform.DOScale(Vector3.zero, duration)
.SetEase(Ease.InBack)
.OnComplete(() =>
{
currentState = StatueState.Broken;
gameObject.SetActive(false);
});
// 播放碎裂音效
PlayBreakSound();
}
#endregion
#region
private void PlayHitSound()
{
if (!string.IsNullOrEmpty(hitSound))
{
MasterAudio.PlaySound3DAtTransform(hitSound, transform);
}
}
private void PlayBreakSound()
{
if (!string.IsNullOrEmpty(breakSound))
{
MasterAudio.PlaySound3DAtTransform(breakSound, transform);
}
}
private void PlayEvilLaughSound()
{
if (!string.IsNullOrEmpty(evilLaughSound))
{
MasterAudio.PlaySound3DAtTransform(evilLaughSound, transform);
}
}
#endregion
#region
private void PlayHitEffect()
{
if (hitParticle != null)
{
hitParticle.Play();
}
// 播放受击震动效果
PlayHitShake();
}
/// <summary>
/// 播放受击震动效果
/// </summary>
private void PlayHitShake()
{
// 使用DOShakePosition实现震动
transform.DOShakePosition(
hitShakeDuration,
hitShakeStrength,
hitShakeVibrato,
hitShakeRandomness,
false, // 不震Y轴
true // 随机方向
);
// 同时添加轻微的旋转震动
transform.DOShakeRotation(
hitShakeDuration,
hitShakeStrength * 10f, // 旋转幅度稍大
hitShakeVibrato / 2,
hitShakeRandomness,
true
);
}
private void PlayBreakEffect()
{
if (breakParticle != null)
{
breakParticle.Play();
}
}
private void PlayHitAnimation()
{
if (animator != null)
{
animator.SetTrigger(hitAnimTrigger);
}
}
private void PlayBreakAnimation()
{
if (animator != null)
{
animator.SetTrigger(breakAnimTrigger);
}
// 碎裂缩放效果
transform.DOScale(Vector3.zero, breakDuration)
.SetEase(Ease.InBack)
.OnComplete(() =>
{
gameObject.SetActive(false);
});
}
/// <summary>
/// 更新裂痕视觉效果
/// </summary>
private void UpdateCrackVisual(int level)
{
// 更新裂痕贴花
for (int i = 0; i < crackDecals.Length; i++)
{
if (crackDecals[i] != null)
{
crackDecals[i].SetActive(i < level);
}
}
// 更新材质(如果有多个裂痕材质)
if (crackMaterials != null && crackMaterials.Length > level && statueRenderer != null)
{
statueRenderer.material = crackMaterials[level];
}
}
#endregion
#region
/// <summary>
/// 获取当前血量
/// </summary>
public int GetCurrentHealth()
{
return currentHealth;
}
/// <summary>
/// 获取裂痕等级
/// </summary>
public int GetCrackLevel()
{
return crackLevel;
}
/// <summary>
/// 是否已碎裂
/// </summary>
public bool IsBroken()
{
return currentState == StatueState.Broken;
}
/// <summary>
/// 重置雕像状态
/// </summary>
public void Reset()
{
currentHealth = maxHealth;
crackLevel = 0;
canHit = true;
currentState = StatueState.Idle;
// 重置缩放
transform.localScale = Vector3.one;
// 重置裂痕
UpdateCrackVisual(0);
// 显示对象
gameObject.SetActive(true);
}
/// <summary>
/// 设置可击打状态
/// </summary>
public void SetCanHit(bool value)
{
canHit = value;
}
#endregion
#region
#if UNITY_EDITOR
[ContextMenu("测试:击打雕像")]
private void TestHit()
{
OnPlayerHit();
}
[ContextMenu("测试:碎裂雕像")]
private void TestBreak()
{
Break();
}
[ContextMenu("测试:播放狞笑")]
private void TestEvilLaugh()
{
PlayEvilLaugh();
}
[ContextMenu("测试:重置雕像")]
private void TestReset()
{
Reset();
}
[ContextMenu("测试:强制消失")]
private void TestForceDisappear()
{
ForceDisappear();
}
#endif
#endregion
}