157 lines
5.1 KiB
C#
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;
|
|
}
|
|
}
|
|
} |