using UnityEngine; using BaseGames.Combat; using BaseGames.Feedback; namespace BaseGames.Player { /// /// 武器 HitBox 实例。 /// 由 WeaponManager 在切换武器时实例化到角色的 [WeaponSocket] 子节点下。 /// /// Prefab 层级示例(每段连击独立一个子节点,在 Prefab 内可视化编辑 Collider2D): /// [WPN_SkyBlade_HitBox] ← WeaponHitBoxInstance /// ├── [HitBox_Ground] Id="" ← 方向默认(ComboStepConfig.hitBoxId 为空时使用) /// ├── [HitBox_Ground_1] Id="g1" ← 第 1 段专属判定 /// ├── [HitBox_Ground_2] Id="g2" ← 第 2 段专属判定 /// ├── [HitBox_Up] Id="" ← 上劈默认 /// ├── [HitBox_Down] Id="" ← 下劈默认 /// └── [HitBox_Air] Id="" ← 空中默认 /// /// ComboStepConfig.hitBoxId 留空 → 用方向默认;填 Id → 精确激活对应子节点。 /// public class WeaponHitBoxInstance : MonoBehaviour { [Header("方向默认 HitBox(hitBoxId 为空时使用)")] [SerializeField] private HitBox _hitBoxGround; [SerializeField] private HitBox _hitBoxUp; [SerializeField] private HitBox _hitBoxDown; [SerializeField] private HitBox _hitBoxAir; private HitBox[] _allHitBoxes; private AttackDirection _activeDir; private IFeedbackPlayer _feedback; /// 下劈命中确认事件(供 DownAttackState Pogo 逻辑)。 public event System.Action OnDownHitConfirmed; /// 任意 HitBox 命中确认事件(供 PlayerCombat 订阅通用命中反馈)。 public event System.Action OnHitConfirmed; private void Awake() { _allHitBoxes = GetComponentsInChildren(true); foreach (var hb in _allHitBoxes) hb.OnHitConfirmed += OnAnyHitConfirmed; _feedback = GetComponentInChildren() ?? NullFeedbackPlayer.Instance; } private void OnAnyHitConfirmed(DamageInfo info) { var weight = info.FinalDamage <= 5 ? HitWeight.Light : info.FinalDamage <= 15 ? HitWeight.Medium : HitWeight.Heavy; _feedback.PlayHit(weight); OnHitConfirmed?.Invoke(info); if (_activeDir == AttackDirection.Down) OnDownHitConfirmed?.Invoke(info); } // ── 公共 API ────────────────────────────────────────────────────────── /// /// 激活 HitBox。 /// hitBoxId 非空时按 Id 精确查找 Prefab 子节点;空 = 使用该方向的默认 HitBox。 /// public void Activate(AttackDirection dir, DamageSourceSO source, Transform attacker, string hitBoxId = "") { _activeDir = dir; _feedback.PlayAttackWhoosh(); var hitBox = string.IsNullOrEmpty(hitBoxId) ? GetHitBox(dir) : (GetHitBoxById(hitBoxId) ?? GetHitBox(dir)); hitBox?.Activate(source, attacker); } /// 停用指定方向的默认 HitBox。 public void Deactivate(AttackDirection dir) => GetHitBox(dir)?.Deactivate(); /// 停用 Prefab 中所有 HitBox。 public void DeactivateAll() { if (_allHitBoxes == null) return; foreach (var hb in _allHitBoxes) hb.Deactivate(); } /// 按 HitBox.Id 查找子节点(未找到返回 null)。 public HitBox GetHitBoxById(string id) { if (_allHitBoxes == null || string.IsNullOrEmpty(id)) return null; foreach (var hb in _allHitBoxes) if (hb.Id == id) return hb; return null; } /// 按攻击方向返回默认 HitBox。 public HitBox GetHitBox(AttackDirection dir) => dir switch { AttackDirection.Ground => _hitBoxGround, AttackDirection.Up => _hitBoxUp, AttackDirection.Down => _hitBoxDown, AttackDirection.Air => _hitBoxAir, _ => null, }; } }