From ef7ff63b6743265af209c6ed3413edd7b3218c99 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Fri, 12 Jun 2026 10:15:27 +0800 Subject: [PATCH] =?UTF-8?q?feat(combat):=20HitBox=20=E6=96=B0=E5=A2=9E=20?= =?UTF-8?q?=5FtargetLayers=20=E5=AE=9E=E4=BE=8B=E7=BA=A7=E4=BC=A4=E5=AE=B3?= =?UTF-8?q?=E7=9B=AE=E6=A0=87=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Physics2D 碰撞矩阵只决定哪些层之间产生 Trigger 事件(全局门),无法表达单个判定盒的伤害目标差异。新增 _targetLayers 掩码:事件发生后仅对配置层结算伤害,拼刀检测独立于该过滤;Enter 入口同步过滤,非目标不进入命中/占用跟踪。默认 Everything,存量资产行为不变。 ENM_CaoZhi 接触伤害盒按规范配置 _targetLayers=PlayerHurtBox(不误伤友军),并清掉误配为 Player 层的 _rivalHitBoxMask(身体接触不参与拼刀)。已实机验证收紧后接触伤害正常。 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Prefabs/Enemies/E001/ENM_CaoZhi.prefab | 5 +++ Assets/_Game/Scripts/Combat/HitBox.cs | 41 +++++++++++++------ 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/Assets/_Game/Prefabs/Enemies/E001/ENM_CaoZhi.prefab b/Assets/_Game/Prefabs/Enemies/E001/ENM_CaoZhi.prefab index 82a8968..a8e6f67 100644 --- a/Assets/_Game/Prefabs/Enemies/E001/ENM_CaoZhi.prefab +++ b/Assets/_Game/Prefabs/Enemies/E001/ENM_CaoZhi.prefab @@ -657,6 +657,11 @@ MonoBehaviour: m_EditorClassIdentifier: _defaultSource: {fileID: 11400000, guid: caae9c7600281fe4e8d8637fa3fd2ca1, type: 2} _hitCooldown: 0.1 + _hitMode: 0 + _hitInterval: 0.5 + _targetLayers: + serializedVersion: 2 + m_Bits: 32768 _id: _rivalHitBoxMask: serializedVersion: 2 diff --git a/Assets/_Game/Scripts/Combat/HitBox.cs b/Assets/_Game/Scripts/Combat/HitBox.cs index 0500d88..6a6568f 100644 --- a/Assets/_Game/Scripts/Combat/HitBox.cs +++ b/Assets/_Game/Scripts/Combat/HitBox.cs @@ -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; + /// /// 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(out var hurtBox)) {