Files
Hasebe/Assets/Code/Infrastructure/Utils/HitBoxUtil.cs
T

157 lines
5.1 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
namespace Code.Infrastructure.Utils
{
public static class HitBoxUtil
{
private struct ActiveHitbox
{
public Matrix4x4 Matrix;
public Material Material;
public float ExpireTime;
}
private static readonly List<ActiveHitbox> _activeBoxes = new();
private static Mesh _unitCubeMesh;
private static bool _isInitialized;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Initialize()
{
if (_isInitialized) return;
_unitCubeMesh = CreateUnitCubeMesh();
// Подписываемся на этап рендера для отрисовки в каждом кадре
RenderPipelineManager.beginContextRendering += OnBeginContextRendering;
UnityEngine.Camera.onPreCull += OnPreCullLegacy;
_isInitialized = true;
}
/// <summary>
/// Добавляет хитбокс на заданную длительность (в секундах)
/// </summary>
public static void DrawBox(Vector3 center, Vector3 size, Quaternion rotation, Material material,
float duration = 1.0f)
{
if (material == null || _unitCubeMesh == null) return;
Matrix4x4 trs = Matrix4x4.TRS(center, rotation, size);
_activeBoxes.Add(new ActiveHitbox
{
Matrix = trs,
Material = material,
ExpireTime = Time.unscaledTime + duration
});
}
/// <summary>
/// Перегрузка напрямую для BoxCollider
/// </summary>
public static void DrawBoxCollider(BoxCollider box, Material material, float duration = 1.0f)
{
if (box == null) return;
Transform t = box.transform;
Vector3 center = t.TransformPoint(box.center);
Vector3 size = Vector3.Scale(box.size, t.lossyScale);
DrawBox(center, size, t.rotation, material, duration);
}
/// <summary>
/// Мгновенная очистка всех активных хитбоксов (например, при выходе из боя)
/// </summary>
public static void Clear()
{
_activeBoxes.Clear();
}
private static void OnBeginContextRendering(ScriptableRenderContext context, List<UnityEngine.Camera> cameras)
{
RenderHitboxes();
}
private static void OnPreCullLegacy(UnityEngine.Camera cam)
{
// Для совместимости с Built-in RP
if (GraphicsSettings.currentRenderPipeline == null)
{
RenderHitboxes();
}
}
private static void RenderHitboxes()
{
if (_activeBoxes.Count == 0) return;
float currentTime = Time.unscaledTime;
for (int i = _activeBoxes.Count - 1; i >= 0; i--)
{
ActiveHitbox box = _activeBoxes[i];
if (currentTime >= box.ExpireTime)
{
_activeBoxes.RemoveAt(i);
continue;
}
// Отправляем GPU команду на отрисовку процедурного меша куба
Graphics.DrawMesh(
_unitCubeMesh,
box.Matrix,
box.Material,
0, // Layer
null, // Camera (null = все камеры)
0, // Submesh index
null, // MaterialPropertyBlock
castShadows: false,
receiveShadows: false,
useLightProbes: false
);
}
}
/// <summary>
/// Генерирует единичный куб 1x1x1 от -0.5 до 0.5 без создания лишних GameObject
/// </summary>
private static Mesh CreateUnitCubeMesh()
{
Mesh mesh = new Mesh { name = "Procedural_Unit_Cube" };
Vector3[] vertices =
{
new(-0.5f, -0.5f, -0.5f), new(0.5f, -0.5f, -0.5f), new(0.5f, 0.5f, -0.5f), new(-0.5f, 0.5f, -0.5f),
new(-0.5f, -0.5f, 0.5f), new(0.5f, -0.5f, 0.5f), new(0.5f, 0.5f, 0.5f), new(-0.5f, 0.5f, 0.5f)
};
int[] triangles =
{
// Задняя
0, 2, 1, 0, 3, 2,
// Передняя
4, 5, 6, 4, 6, 7,
// Левая
0, 7, 3, 0, 4, 7,
// Правая
1, 2, 6, 1, 6, 5,
// Нижняя
0, 1, 5, 0, 5, 4,
// Верхняя
3, 6, 2, 3, 7, 6
};
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.RecalculateNormals();
mesh.RecalculateBounds();
return mesh;
}
}
}