Files
Hasebe/Assets/Code/OfficeGame/BattleMode/HitBoxes/EntityHitBoxes.cs
T

103 lines
3.3 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
namespace Code.OfficeGame.BattleMode.HitBoxes
{
public class EntityHitBoxes : MonoBehaviour
{
[Serializable]
public struct HitBoxPartCollider
{
public HitBoxPartType PartType;
public BoxCollider Collider;
}
[SerializeField] private HitBoxPartCollider[] bodyParts;
private LayerMask _hitBoxLayerMask;
private readonly Dictionary<HitBoxPartType, BoxCollider> _colliderMap = new();
private readonly Collider[] _hitResults = new Collider[8];
// Список уже поражённых целей за один взмах, чтобы один удар не наносил урон 8 раз за 8 активных кадров
private readonly HashSet<GameObject> _alreadyHitTargets = new();
private HitBoxPartType? _activePart;
private void Awake()
{
foreach (var part in bodyParts)
{
part.Collider.enabled = false;
_colliderMap[part.PartType] = part.Collider;
}
_hitBoxLayerMask = LayerMask.GetMask("HitBox");
}
public void EnableHitbox(HitBoxPartType part)
{
if (_activePart == part) return;
_alreadyHitTargets.Clear();
_activePart = part;
if (_colliderMap.TryGetValue(part, out var col))
{
col.enabled = true;
}
}
public void DisableAllHitboxes()
{
if (_activePart.HasValue && _colliderMap.TryGetValue(_activePart.Value, out var col))
{
col.enabled = false;
}
_activePart = null;
_alreadyHitTargets.Clear();
}
public void CheckHitsForCurrentFrame(Action<Collider> onNewTargetHit)
{
if (!_activePart.HasValue) return;
BoxCollider box = _colliderMap[_activePart.Value];
Transform boxTransform = box.transform;
// Вычисляем точный мировой центр и размер коллайдера на кости в текущем кадре анимации
Vector3 worldCenter = boxTransform.TransformPoint(box.center);
Vector3 worldHalfExtents = Vector3.Scale(box.size * 0.5f, boxTransform.lossyScale);
int hitsCount = Physics.OverlapBoxNonAlloc(
worldCenter,
worldHalfExtents,
_hitResults,
boxTransform.rotation,
_hitBoxLayerMask,
QueryTriggerInteraction.Collide);
for (int i = 0; i < hitsCount; i++)
{
Collider hitCol = _hitResults[i];
// Игнорируем самого себя
if (hitCol.transform.root == transform.root) continue;
// Если за этот взмах мы ещё не били этого врага — засчитываем попадание!
if (_alreadyHitTargets.Add(hitCol.gameObject))
{
onNewTargetHit?.Invoke(hitCol);
}
}
}
}
public enum HitBoxPartType
{
Head,
Chest,
Arm,
Leg
}
}