103 lines
3.3 KiB
C#
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
|
|
}
|
|
} |