Files
zeling_v2/Assets/Scripts/Combat/HitBox.cs
2026-05-08 11:04:00 +08:00

97 lines
3.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 System.Collections.Generic;
using UnityEngine;
namespace BaseGames.Combat
{
/// <summary>
/// 攻击判定盒。挂载在武器 Prefab 或技能 HitBox Prefab 的子节点上。
/// Phase 1 简化:直接挂在 Player Prefab 子节点 [HitBoxGround/Up/Down/Air]。
/// Collider2D 需设 IsTrigger = trueLayer = PlayerHitBox 或 EnemyHitBox。
/// </summary>
[RequireComponent(typeof(Collider2D))]
public class HitBox : MonoBehaviour
{
[SerializeField] private DamageSourceSO _defaultSource;
[SerializeField] private float _hitCooldown = 0.1f;
private DamageSourceSO _currentSource;
private Transform _attackerTransform;
private bool _isActive;
/// <summary>命中确认委托PlayerCombat / EnemyCombat 订阅)。</summary>
public System.Action<DamageInfo> OnHitConfirmed;
/// <summary>
/// 激活 HitBox。source/attacker 均可选,未传则使用 Inspector 默认值。
/// ⚠️ 不存在 Activate(float duration) 重载。
/// </summary>
public void Activate(DamageSourceSO source = null, Transform attacker = null)
{
_currentSource = source ?? _defaultSource;
_attackerTransform = attacker ?? transform;
_isActive = true;
}
public void Deactivate() => _isActive = false;
private void Awake()
{
// 确保 Collider2D 是 Trigger
var col = GetComponent<Collider2D>();
if (!col.isTrigger)
Debug.LogWarning($"[HitBox] {name}: Collider2D.isTrigger 应为 true。", this);
}
private void OnDisable()
{
_isActive = false;
_hitCooldownTimers.Clear();
}
private void OnTriggerEnter2D(Collider2D other)
{
if (!_isActive) return;
if (_currentSource == null)
{
Debug.LogWarning($"[HitBox] {name}: 无 DamageSourceSO跳过命中。", this);
return;
}
if (!CheckCooldown(other)) return;
Vector2 knockDir = ((Vector2)other.bounds.center
- (Vector2)_attackerTransform.position).normalized;
// ⚡ 零 GCstruct 工厂,就地赋值运行时字段
var info = DamageInfo.From(_currentSource);
info.KnockbackDirection = knockDir;
info.KnockbackForce = _currentSource.KnockbackForce;
info.SourcePosition = _attackerTransform.position;
info.SourceLayer = _attackerTransform.gameObject.layer;
// ① 命中 HurtBox
var hurtBox = other.GetComponent<HurtBox>();
if (hurtBox != null)
{
hurtBox.ReceiveDamage(info);
OnHitConfirmed?.Invoke(info);
return;
}
// ② 命中 IBreakable机关/障碍物)
other.GetComponent<IBreakable>()?.TryInteract(info);
}
// ── 同目标多帧命中冷却 ────────────────────────────────────────────────
private readonly Dictionary<Collider2D, float> _hitCooldownTimers = new();
private bool CheckCooldown(Collider2D other)
{
float now = Time.time;
if (_hitCooldownTimers.TryGetValue(other, out float last) && now - last < _hitCooldown)
return false;
_hitCooldownTimers[other] = now;
return true;
}
}
}