Files
zeling_v2/Assets/_Game/Scripts/Animation/PlayerAnimationEvents.cs

150 lines
6.1 KiB
C#
Raw Permalink 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 System;
using Animancer;
using BaseGames.Combat;
using BaseGames.Feedback;
using BaseGames.Parry;
using BaseGames.Player;
using UnityEngine;
namespace BaseGames.Animation
{
/// <summary>
/// 玩家动画事件接收器(架构 §AnimationModule
/// 挂载于玩家 Prefab 根节点。负责将 Animancer 动画时间点回调路由到
/// HitBox 激活、招架窗口、无敌帧、移动取消窗口、反馈等系统。
///
/// 使用方式:
/// 1. 在 Inspector 中填充 _hitBoxes、_hurtBox、_mover、_parrySystem。
/// 2. 将每条 ClipTransition + AnimationEventConfigSO 配对添加到 _bindings。
/// 3. Awake 中 AnimationEventBinder.Bind 自动完成注入。
/// </summary>
public class PlayerAnimationEvents : MonoBehaviour, IAnimationEventHandler
{
[Serializable]
public struct EventBinding
{
[Tooltip("Animancer ClipTransition需与动画组件中同一引用绑定。")]
public ClipTransition clip;
[Tooltip("对应的 AnimationEventConfigSO 资产。")]
public AnimationEventConfigSO config;
}
[Header("子系统引用")]
[SerializeField] private HitBox[] _hitBoxes;
[SerializeField] private HurtBox _hurtBox;
[SerializeField] private PlayerMovement _mover;
[SerializeField] private ParrySystem _parrySystem;
[Header("事件绑定(每个 Clip 对应一个配置资产)")]
[SerializeField] private EventBinding[] _bindings;
private IFeedbackPlayer _feedback;
private void Awake()
{
// IFeedbackPlayer 由父层或同层实现PlayerFeedback / NullFeedbackPlayer
_feedback = GetComponentInParent<IFeedbackPlayer>()
?? NullFeedbackPlayer.Instance;
foreach (var b in _bindings)
AnimationEventBinder.Bind(b.clip, b.config, this);
}
// ── IAnimationEventHandler ─────────────────────────────────────────
public void HandleEvent(AnimationEventType type, string payload)
{
switch (type)
{
// ── 命中判定 ──────────────────────────────────────────────
case AnimationEventType.EnableHitBox:
SetHitBoxActive(payload, true);
break;
case AnimationEventType.DisableHitBox:
SetHitBoxActive(payload, false);
break;
case AnimationEventType.AttackImpact:
_feedback.PlayAttackWhoosh();
break;
// ── 招架窗口 ──────────────────────────────────────────────
case AnimationEventType.EnableParryWindow:
_parrySystem?.OpenParryWindow();
break;
case AnimationEventType.DisableParryWindow:
_parrySystem?.CloseParryWindow();
break;
// ── 无敌帧 ────────────────────────────────────────────────
case AnimationEventType.EnableIFrame:
_hurtBox?.SetInvincible(true);
break;
case AnimationEventType.DisableIFrame:
_hurtBox?.SetInvincible(false);
break;
// ── 移动反馈 ──────────────────────────────────────────────
case AnimationEventType.LandImpact:
_feedback.PlayLandImpact();
break;
case AnimationEventType.JumpLaunch:
_feedback.PlayJumpLaunch();
break;
case AnimationEventType.Footstep:
_feedback.PlayFootstep();
break;
// ── 取消窗口 ──────────────────────────────────────────────
case AnimationEventType.CancelWindowOpen:
_mover?.SetCancelWindowOpen(true);
break;
case AnimationEventType.CancelWindowClose:
_mover?.SetCancelWindowOpen(false);
break;
// ── 通用反馈 ──────────────────────────────────────────────
case AnimationEventType.TriggerFeedback:
if (!string.IsNullOrEmpty(payload))
_feedback.TriggerPreset(payload);
break;
case AnimationEventType.PlaySFX:
if (!string.IsNullOrEmpty(payload))
_feedback.PlaySFXById(payload);
break;
}
}
// ── 私有辅助 ───────────────────────────────────────────────────────
/// <summary>
/// 按 Id 激活或停用 HitBox。
/// payload 为空时操作所有 HitBox非空时仅操作 Id 匹配的 HitBox。
/// </summary>
private void SetHitBoxActive(string id, bool active)
{
if (_hitBoxes == null) return;
bool matchAll = string.IsNullOrEmpty(id);
foreach (var hb in _hitBoxes)
{
if (hb == null) continue;
if (matchAll || hb.Id == id)
{
if (active) hb.Activate();
else hb.Deactivate();
}
}
}
}
}