using System.Collections.Generic; using UnityEngine; [RequireComponent(typeof(CircleCollider2D))] public class AttackHitbox : MonoBehaviour { private CircleCollider2D _collider; private int _damage; private Vector2 _hitVelocity; private Vector2 _hitSourcePosition; private Vector2? _hitTargetPosition; private float _hitPositionMinDistance; private bool _correctHitTargetY; private int _hitPositionSolidMask; private float _hitPositionCorrectionDuration; private string _hitReactionState; private LayerMask _targetLayer; private readonly HashSet _alreadyHit = new(); private void Awake() { _collider = GetComponent(); // 플레이어 몸체는 적과 물리 충돌하지 않으므로, 공격 판정은 이 트리거만 사용한다. _collider.isTrigger = true; _collider.enabled = false; } public void Activate(ActionData data, Vector2 localPosition, Vector2 hitVelocity, Vector2 sourcePosition, Vector2? hitTargetPosition, bool correctHitTargetY, int hitPositionSolidMask, LayerMask targetLayer) { transform.localPosition = localPosition; _collider.radius = data.Radius; _damage = data.Damage; _hitVelocity = hitVelocity; _hitSourcePosition = sourcePosition; _hitTargetPosition = hitTargetPosition; _hitPositionMinDistance = Mathf.Abs(data.HitTargetOffset.x); _correctHitTargetY = correctHitTargetY; _hitPositionSolidMask = hitPositionSolidMask; _hitPositionCorrectionDuration = data.HitPositionCorrectionDuration; _hitReactionState = data.HitReactionAnimationState; _targetLayer = targetLayer; _alreadyHit.Clear(); _collider.enabled = true; // 판정이 켜진 순간 이미 범위 안에 있던 적도 같은 프레임에 잡아낸다. ScanImmediateOverlap(); } public void Deactivate() { _collider.enabled = false; _alreadyHit.Clear(); } private void ScanImmediateOverlap() { Collider2D[] hits = Physics2D.OverlapCircleAll(transform.position, _collider.radius, _targetLayer); foreach (var hit in hits) TryDamage(hit); } private void OnTriggerEnter2D(Collider2D other) => TryDamage(other); private void OnTriggerStay2D(Collider2D other) => TryDamage(other); private void TryDamage(Collider2D other) { if ((_targetLayer.value & (1 << other.gameObject.layer)) == 0) return; // 피격 콜라이더는 자식에 있고, 데미지 처리는 루트에 있을 수 있다. if (!other.TryGetComponent(out var target)) target = other.GetComponentInParent(); if (target == null) return; if (_alreadyHit.Contains(target)) return; _alreadyHit.Add(target); Vector2? targetPosition = GetCorrectionTargetPosition(other); target.TakeDamage(_damage, _hitVelocity, _hitReactionState, targetPosition, _correctHitTargetY, _hitPositionSolidMask, _hitPositionCorrectionDuration); } private Vector2? GetCorrectionTargetPosition(Collider2D other) { if (!_hitTargetPosition.HasValue) return null; if (_hitPositionMinDistance <= 0.001f) return null; Vector2 currentPosition = other.attachedRigidbody != null ? other.attachedRigidbody.position : (Vector2)other.transform.root.position; float currentDistance = Mathf.Abs(currentPosition.x - _hitSourcePosition.x); return currentDistance < _hitPositionMinDistance ? _hitTargetPosition : null; } }