- Implemented HurtBoxOwnerGuard to ensure that multiple HurtBoxes on the same character do not register damage multiple times during a single HitBox activation. - Added custom editor for HitBox to facilitate the creation of shape colliders with HitBoxColliderProxy. - Introduced PhysicsPerceptionSystem for enemy perception, supporting multiple detection modes including RangeCircle, BatchLOS, FanCast, and BoxCast. - Created EnemyPatrolZone to define patrol and chase areas for enemies, allowing for shared zones among multiple enemies. - Added BD_IsOutsideZone conditional task for Behavior Designer to check if an enemy or player is outside a defined patrol zone.
46 lines
2.0 KiB
C#
46 lines
2.0 KiB
C#
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
|
||
namespace BaseGames.Combat
|
||
{
|
||
/// <summary>
|
||
/// HurtBox 所有者保护组件。防止同一角色身上的多个 HurtBox 在同一次 HitBox 激活中被重复伤害。
|
||
///
|
||
/// 工作原理:每次 HitBox 激活携带唯一 ActivationId(由 HitBox.Activate() 生成递增值)。
|
||
/// HurtBox.ReceiveDamage 调用 TryRegisterHit(id) 时,同一 id 只有首次调用返回 true;
|
||
/// 后续同 id 调用(来自同一角色的其他 HurtBox 子节点)返回 false 并跳过伤害流水线,
|
||
/// 从而保证"多个 HurtBox 共享同一 HP 池时,一次攻击只扣一次血"。
|
||
///
|
||
/// 生命周期:由 HurtBox.Awake() 在角色根节点自动添加(如不存在则创建)。无需手动挂载。
|
||
/// 逐帧通过 frameCount 差异懒清空处理集,零 GC 开销。
|
||
/// </summary>
|
||
[AddComponentMenu("")] // 隐藏菜单:由 HurtBox 自动管理
|
||
public sealed class HurtBoxOwnerGuard : MonoBehaviour
|
||
{
|
||
private readonly HashSet<uint> _processedIds = new(4);
|
||
private int _lastClearFrame = -1;
|
||
|
||
/// <summary>
|
||
/// 尝试注册一次命中。
|
||
/// </summary>
|
||
/// <param name="activationId">HitBox 激活 ID(0 = 无追踪路径,始终允许通过)。</param>
|
||
/// <returns>true = 首次注册,应继续处理伤害;false = 同 id 已被处理,跳过。</returns>
|
||
public bool TryRegisterHit(uint activationId)
|
||
{
|
||
// activationId == 0:LethalTrap / BodyContactDamage 等旁路路径,不做去重
|
||
if (activationId == 0) return true;
|
||
|
||
EnsureClearedThisFrame();
|
||
return _processedIds.Add(activationId);
|
||
}
|
||
|
||
private void EnsureClearedThisFrame()
|
||
{
|
||
int frame = Time.frameCount;
|
||
if (frame == _lastClearFrame) return;
|
||
_processedIds.Clear();
|
||
_lastClearFrame = frame;
|
||
}
|
||
}
|
||
}
|