fix(combat): HitBox 命中节奏统一 + 投射物穿透可配

HitBox 新增 HitMode Single/Interval:Interval 用 Enter/Exit 跟踪占用 + Update 轮询,对停留目标按间隔重判,不再依赖 OnTriggerEnter 单次语义。BodyContactDamage 改用 Interval 模式,修复停留在接触判定内、无敌结束后不再受伤的 bug;FlyingEnemy 接触伤害加按目标间隔节流。ProjectileConfigSO 新增 MaxHits 默认 1 即命中即消失,Projectile 按命中预算回收,修掉默认无限穿透;弹反守卫避免反射后立即回收。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-11 16:31:27 +08:00
parent 0491e3f919
commit cc046c53b3
5 changed files with 124 additions and 14 deletions

View File

@@ -21,6 +21,12 @@ namespace BaseGames.Combat
// Lifetime 在 Initialize 时缓存,避免 Update 每帧访问 SO 成员并做 null check
private float _lifetime = float.MaxValue;
// 命中预算:剩余可命中次数(<=0 的 _maxHits 表示无限穿透)
private int _maxHits = 1;
private int _hitsRemaining;
// 弹反帧标记:弹反命中不计入命中预算(避免反射后立即被回收)
private bool _justReflected;
private PooledObject _pooledObject;
protected virtual void Awake()
@@ -28,6 +34,8 @@ namespace BaseGames.Combat
_rb = GetComponent<Rigidbody2D>();
_hitBox = GetComponent<HitBox>();
_pooledObject = GetComponent<PooledObject>();
// 订阅命中确认:按命中预算决定何时回收(穿透 / 命中即消失)
_hitBox.OnHitConfirmed += HandleHitConfirmed;
}
/// <summary>从对象池取出后的初始化入口。</summary>
@@ -38,6 +46,9 @@ namespace BaseGames.Combat
DamageInfo = damageInfo;
Direction = direction.normalized;
_aliveTimer = 0f;
_maxHits = config.MaxHits;
_hitsRemaining = config.MaxHits;
_justReflected = false;
SetFactionLayer(ownerLayer);
_hitBox.Activate(config.DamageSource);
@@ -71,6 +82,18 @@ namespace BaseGames.Combat
// 重置 HitBox 命中记录,确保反射后可命中新目标
_hitBox.Deactivate();
_hitBox.Activate(_config?.DamageSource);
// 反射后重置命中预算,并跳过本次(弹反)命中的扣减,避免反射后立即被回收
_hitsRemaining = _maxHits;
_justReflected = true;
}
/// <summary>HitBox 命中确认回调:按命中预算决定是否回收(穿透 / 命中即消失)。</summary>
private void HandleHitConfirmed(DamageInfo _)
{
if (_maxHits <= 0) return; // 无限穿透:只靠寿命回收
if (_justReflected) { _justReflected = false; return; } // 弹反命中不计入预算
if (--_hitsRemaining <= 0) ReturnToPool();
}
protected virtual void OnInitialized() { }