Files
zeling_v2/Assets/_Game/Scripts/Combat/DamageInfo.cs
Joywayer 06048c966a feat: Add HurtBoxOwnerGuard to prevent multiple damage registrations from the same HitBox activation
- 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.
2026-06-02 16:10:44 +08:00

143 lines
7.6 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.Core.Events;
namespace BaseGames.Combat
{
/// <summary>
/// 单次伤害信息。流水线RawDamage → Amount护盾修改→ FinalDamage防御减免后
/// ⚠️ 保留为可变 structHurtBox 流水线需要在方法内修改本地副本的 Amount / FinalDamage。
/// Builder 通过独立字段构造,不直接修改 DamageInfo 实例。
/// </summary>
[System.Serializable]
public struct DamageInfo
{
public int RawDamage; // HitBox 设定的原始值(工厂/Builder 写入一次)
public int Amount; // 流水线中被护盾/防御修改
public int FinalDamage; // HurtBox 写入,最终 HP 扣除量
public Vector2 KnockbackDirection;
public float KnockbackForce;
public float HitStunDuration;
public DamageType Type;
public DamageCategory Category;
public DamageFlags Flags;
public DamageTags Tags;
public Vector2 SourcePosition;
public int SourceLayer;
public HitFxType FxType;
public BreakLevel Break;
public string SourceId;
public string SkillId;
/// <summary>
/// HitBox 激活实例 ID由 HitBox.Activate() 自动生成并写入0 = 无追踪路径)。
/// HurtBoxOwnerGuard 用此字段做同激活去重,防止多 HurtBox 节点被同一次攻击重复扣血。
/// [NonSerialized]:每次激活动态生成,不需要序列化。
/// </summary>
[System.NonSerialized] public uint HitActivationId;
/// <summary>
/// 攻击来源投射物(仅当攻击方是 Projectile 时非 null
/// 用于弹反成功时调用 ReflectAsPlayerProjectile() 翻转阵营。
/// [NonSerialized]MonoBehaviour 引用不参与 Unity 资产序列化。
/// </summary>
[System.NonSerialized] public Projectile SourceProjectile;
// ── Builder ──────────────────────────────────────────────────────────
/// <summary>
/// 通过独立字段构造 DamageInfo避免直接持有可变 DamageInfo 实例。
/// </summary>
public class Builder
{
private int _raw;
private DamageType _type;
private DamageCategory _category;
private DamageFlags _flags;
private DamageTags _tags;
private string _skillId;
private string _sourceId;
private Vector2 _knockbackDirection;
private float _knockbackForce;
private float _hitStunDuration;
private HitFxType _fxType;
private BreakLevel _break;
private Vector2 _sourcePosition;
private int _sourceLayer;
private Projectile _sourceProjectile;
public Builder() { }
// SetRaw 同步初始化 AmountAmount 始终以 RawDamage 为起点)
public Builder SetRaw(int v) { _raw = v; return this; }
public Builder SetType(DamageType v) { _type = v; return this; }
public Builder SetCategory(DamageCategory v) { _category = v; return this; }
public Builder SetFlags(DamageFlags v) { _flags = v; return this; }
public Builder SetTags(DamageTags v) { _tags = v; return this; }
public Builder SetSkillId(string v) { _skillId = v; return this; }
public Builder SetSourceId(string v) { _sourceId = v; return this; }
public Builder SetKnockback(Vector2 dir, float force) { _knockbackDirection = dir; _knockbackForce = force; return this; }
public Builder SetStun(float dur) { _hitStunDuration = dur; return this; }
public Builder SetFx(HitFxType v) { _fxType = v; return this; }
public Builder SetBreak(BreakLevel v) { _break = v; return this; }
public Builder SetSourcePos(Vector2 v) { _sourcePosition = v; return this; }
public Builder SetLayer(int v) { _sourceLayer = v; return this; }
public Builder SetProjectile(Projectile v) { _sourceProjectile = v; return this; }
public DamageInfo Build() => new DamageInfo
{
RawDamage = _raw,
Amount = _raw,
Type = _type,
Category = _category,
Flags = _flags,
Tags = _tags,
SkillId = _skillId,
SourceId = _sourceId,
KnockbackDirection = _knockbackDirection,
KnockbackForce = _knockbackForce,
HitStunDuration = _hitStunDuration,
FxType = _fxType,
Break = _break,
SourcePosition = _sourcePosition,
SourceLayer = _sourceLayer,
SourceProjectile = _sourceProjectile,
};
}
/// <summary>
/// ⚡ 零堆分配工厂(热路径首选)。从 DamageSourceSO 填入所有静态字段;
/// 可选传入运行时字段knockbackDir、sourcePos、sourceLayer
/// 无需调用方事后就地赋值。
/// </summary>
public static DamageInfo From(
DamageSourceSO so,
Vector2 knockbackDir = default,
Vector2 sourcePos = default,
int sourceLayer = 0,
Projectile sourceProjectile = null)
{
int baseAmt = Mathf.RoundToInt(so.BaseDamage * so.DamageMultiplier);
return new DamageInfo
{
RawDamage = baseAmt,
Amount = baseAmt,
Type = so.Type,
Category = so.Category,
Flags = so.Flags,
Tags = so.Tags,
HitStunDuration = so.HitStunDuration,
FxType = so.FxType,
Break = so.BreakLevel,
SourceId = so.sourceId,
SkillId = so.skillId,
KnockbackDirection = knockbackDir,
KnockbackForce = so.KnockbackForce,
SourcePosition = sourcePos,
SourceLayer = sourceLayer,
SourceProjectile = sourceProjectile,
};
}
}
/// <summary>伤害事件频道EVT_DamageDealt。</summary>
[UnityEngine.CreateAssetMenu(menuName = "Events/DamageDealt")]
public class DamageInfoEventChannelSO : BaseEventChannelSO<DamageInfo> { }
}