feat(combat): HitBox 新增 _targetLayers 实例级伤害目标过滤
Physics2D 碰撞矩阵只决定哪些层之间产生 Trigger 事件(全局门),无法表达单个判定盒的伤害目标差异。新增 _targetLayers 掩码:事件发生后仅对配置层结算伤害,拼刀检测独立于该过滤;Enter 入口同步过滤,非目标不进入命中/占用跟踪。默认 Everything,存量资产行为不变。 ENM_CaoZhi 接触伤害盒按规范配置 _targetLayers=PlayerHurtBox(不误伤友军),并清掉误配为 Player 层的 _rivalHitBoxMask(身体接触不参与拼刀)。已实机验证收紧后接触伤害正常。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,12 @@ namespace BaseGames.Combat
|
||||
[Tooltip("Interval 模式下,对同一目标重复造成伤害的间隔(秒)。")]
|
||||
[SerializeField] private float _hitInterval = 0.5f;
|
||||
|
||||
[Header("伤害目标")]
|
||||
[Tooltip("本判定盒可造成伤害的 Layer(实例级过滤,叠加在 Physics2D 碰撞矩阵之上)。" +
|
||||
"矩阵决定哪些层之间会产生 Trigger 事件(全局),此掩码决定事件发生后是否实际结算伤害(单盒)。" +
|
||||
"默认 Everything = 行为与仅靠矩阵时一致。典型配置:EnemyHitBox 盒勾 PlayerHurtBox(不误伤友军则不勾 EnemyHurtBox)。")]
|
||||
[SerializeField] private LayerMask _targetLayers = ~0;
|
||||
|
||||
/// <summary>
|
||||
/// HitBox 标识符,供 PlayerAnimationEvents / EnemyAnimationEvents 按名称精确激活特定判定盒。
|
||||
/// 留空表示"无 Id";事件 payload 为空时将操作所有 HitBox。
|
||||
@@ -203,6 +209,13 @@ namespace BaseGames.Combat
|
||||
Debug.LogWarning($"[HitBox] {name}: 无 DamageSourceSO,跳过命中。", this);
|
||||
return;
|
||||
}
|
||||
// 目标层过滤:既非伤害目标层、也非拼刀对象层时直接忽略,
|
||||
// 不进入命中/占用跟踪(矩阵管"事件是否发生",此掩码管"本盒是否结算")
|
||||
int enterLayer = other.gameObject.layer;
|
||||
bool isDamageTarget = (_targetLayers.value & (1 << enterLayer)) != 0;
|
||||
bool isClashRival = (_rivalHitBoxMask.value & (1 << enterLayer)) != 0 && CanClash;
|
||||
if (!isDamageTarget && !isClashRival) return;
|
||||
|
||||
if (_hitMode == HitMode.Interval)
|
||||
{
|
||||
// 周期接触模式:记录占用并立即结算一次;后续由 Update 按间隔对仍停留的目标重判。
|
||||
@@ -226,18 +239,6 @@ namespace BaseGames.Combat
|
||||
// 排除自身:攻击方与受击方属于同一根 GameObject 时跳过(防止近战 EnemyHitBox 命中同一敌人的 EnemyHurtBox)
|
||||
if (other.transform.root == _attackerTransform.root) return;
|
||||
|
||||
Vector2 knockDir = ((Vector2)other.bounds.center
|
||||
- (Vector2)_attackerTransform.position).normalized;
|
||||
|
||||
// ⚡ 零 GC:struct 工厂,运行时字段内联传入
|
||||
var info = DamageInfo.From(
|
||||
_currentSource,
|
||||
knockDir,
|
||||
_attackerTransform.position,
|
||||
_attackerTransform.gameObject.layer,
|
||||
_ownerProjectile);
|
||||
info.HitActivationId = _currentActivationId;
|
||||
|
||||
// ① 拼刀检测:当前 HitBox 携带 CanClash 标记,且碰到对立阵营的 HitBox 层
|
||||
int otherLayer = other.gameObject.layer;
|
||||
bool isRivalHitBoxLayer = (_rivalHitBoxMask.value & (1 << otherLayer)) != 0;
|
||||
@@ -251,6 +252,22 @@ namespace BaseGames.Combat
|
||||
}
|
||||
}
|
||||
|
||||
// 目标层过滤:拼刀之外,仅对 _targetLayers 内的层结算伤害。
|
||||
// Enter 入口已过滤,这里再守一次:覆盖 Interval 占用期间目标 Layer 被运行时改变的情况(如投射物弹反换层)。
|
||||
if ((_targetLayers.value & (1 << otherLayer)) == 0) return;
|
||||
|
||||
Vector2 knockDir = ((Vector2)other.bounds.center
|
||||
- (Vector2)_attackerTransform.position).normalized;
|
||||
|
||||
// ⚡ 零 GC:struct 工厂,运行时字段内联传入
|
||||
var info = DamageInfo.From(
|
||||
_currentSource,
|
||||
knockDir,
|
||||
_attackerTransform.position,
|
||||
_attackerTransform.gameObject.layer,
|
||||
_ownerProjectile);
|
||||
info.HitActivationId = _currentActivationId;
|
||||
|
||||
// ② 命中 HurtBox
|
||||
if (other.TryGetComponent<HurtBox>(out var hurtBox))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user