Files
zeling_v2/Assets/_Game/Scripts/Player/WeaponHitBoxInstance.cs
Joywayer 8515dfa7ab feat(combat): 下劈弹跳完善——可破坏物可弹跳+独立弹跳力+下劈保留空中操控
- HitBox 新增 OnBreakableHitConfirmed:命中 IBreakable 也发命中确认,
  下劈打可破坏物同样弹跳;不走 OnHitConfirmed,灵力仍只来自敌人
- 新增 PogoBounceForce 配置(默认 15,略低于 JumpForce=18)+
  PlayerMovement.PogoBounce(),弹跳高度独立可调、固定不可截断
- DownAttackState 增加 OnStateFixedUpdate:下劈期间保留完整空中
  水平操控(含与 FallState 同款贴墙保护)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 09:51:17 +08:00

121 lines
5.0 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 BaseGames.Combat;
using BaseGames.Feedback;
namespace BaseGames.Player
{
/// <summary>
/// 武器 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 → 精确激活对应子节点。
/// </summary>
public class WeaponHitBoxInstance : MonoBehaviour
{
[Header("方向默认 HitBoxhitBoxId 为空时使用)")]
[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;
/// <summary>下劈命中确认事件(供 DownAttackState Pogo 逻辑)。命中 HurtBox 或可破坏物均触发。</summary>
public event System.Action<DamageInfo> OnDownHitConfirmed;
/// <summary>任意 HitBox 命中确认事件(供 PlayerCombat 订阅通用命中反馈)。</summary>
public event System.Action<DamageInfo> OnHitConfirmed;
private void Awake()
{
_allHitBoxes = GetComponentsInChildren<HitBox>(true);
foreach (var hb in _allHitBoxes)
{
hb.OnHitConfirmed += OnAnyHitConfirmed;
hb.OnBreakableHitConfirmed += OnAnyBreakableHitConfirmed;
}
_feedback = GetComponentInChildren<IFeedbackPlayer>() ?? 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);
}
/// <summary>
/// 命中可破坏物:播放轻量打击反馈;下劈方向时同样触发弹跳。
/// 不转发 OnHitConfirmed可破坏物不参与灵力获取
/// </summary>
private void OnAnyBreakableHitConfirmed(DamageInfo info)
{
_feedback.PlayHit(HitWeight.Light);
if (_activeDir == AttackDirection.Down)
OnDownHitConfirmed?.Invoke(info);
}
// ── 公共 API ──────────────────────────────────────────────────────────
/// <summary>
/// 激活 HitBox。
/// hitBoxId 非空时按 Id 精确查找 Prefab 子节点;空 = 使用该方向的默认 HitBox。
/// </summary>
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);
}
/// <summary>停用指定方向的默认 HitBox。</summary>
public void Deactivate(AttackDirection dir) => GetHitBox(dir)?.Deactivate();
/// <summary>停用 Prefab 中所有 HitBox。</summary>
public void DeactivateAll()
{
if (_allHitBoxes == null) return;
foreach (var hb in _allHitBoxes) hb.Deactivate();
}
/// <summary>按 HitBox.Id 查找子节点(未找到返回 null。</summary>
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;
}
/// <summary>按攻击方向返回默认 HitBox。</summary>
public HitBox GetHitBox(AttackDirection dir) => dir switch
{
AttackDirection.Ground => _hitBoxGround,
AttackDirection.Up => _hitBoxUp,
AttackDirection.Down => _hitBoxDown,
AttackDirection.Air => _hitBoxAir,
_ => null,
};
}
}