Files
zeling_v2/Assets/_Game/Scripts/Enemies/FlyingEnemy.cs
Joywayer cc046c53b3 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>
2026-06-11 16:31:55 +08:00

79 lines
2.8 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 System.Collections.Generic;
using UnityEngine;
using BaseGames.Combat;
namespace BaseGames.Enemies
{
/// <summary>
/// 飞行敌人基类。
///
/// 导航由 <see cref="Navigation.FlyingDirectNavigator"/> 实现IPathAgent
/// AI 行为逻辑由挂载的 Behavior Designer 树驱动。
/// 本类仅负责:
/// <list type="bullet">
/// <item>Rigidbody2D 无重力初始化</item>
/// <item>接触伤害OnTriggerStay2D</item>
/// <item>StopMovement / MoveInDirection 的 Rigidbody2D 快速移动重写</item>
/// </list>
/// </summary>
public class FlyingEnemy : EnemyBase
{
[Header("飞行移动(快速覆盖速度)")]
[SerializeField] private float _moveSpeed = 3f;
[Header("接触伤害")]
[SerializeField] private DamageSourceSO _contactDamageSource;
[Tooltip("对停留在体接触范围内的同一目标重复造成伤害的间隔(秒)。")]
[SerializeField] private float _contactInterval = 0.5f;
private Rigidbody2D _rb;
// 按目标节流:避免 OnTriggerStay2D 每个物理帧都造成伤害(与 HitBox.Interval 节奏一致)
private readonly Dictionary<HurtBox, float> _contactTimers = new();
protected override void Awake()
{
base.Awake();
_rb = GetComponent<Rigidbody2D>();
if (_rb != null)
{
_rb.gravityScale = 0f;
_rb.constraints = RigidbodyConstraints2D.FreezeRotation;
}
}
public override void StopMovement()
{
if (_rb != null) _rb.velocity = Vector2.zero;
}
public override void MoveInDirection(float dir)
{
if (_rb != null) _rb.velocity = new Vector2(dir * _moveSpeed, 0f);
}
private void OnTriggerStay2D(Collider2D other)
{
if (_contactDamageSource == null) return;
if (CurrentState == EnemyStateType.Dead) return;
if (_rb == null) return;
var hurtBox = other.GetComponent<HurtBox>();
if (hurtBox == null) return;
// 按目标间隔节流:停留接触时每 _contactInterval 秒最多结算一次,
// 不再每个物理帧都调用 ReceiveDamage命中是否生效仍由承伤方无敌帧决定
float now = Time.time;
if (_contactTimers.TryGetValue(hurtBox, out float last) && now - last < _contactInterval) return;
_contactTimers[hurtBox] = now;
Vector2 knockDir = ((Vector2)other.bounds.center - _rb.position).normalized;
var info = DamageInfo.From(
_contactDamageSource,
knockDir,
transform.position,
gameObject.layer);
hurtBox.ReceiveDamage(info);
}
}
}