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

178 lines
6.1 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
namespace Code.OfficeGame.BattleMode.HitBoxes
{
public static class HitboxDrawer
{
private struct ActiveBox
{
public Matrix4x4 Matrix;
public Color Color;
public float ExpireTime;
}
private static readonly List<ActiveBox> _boxes = new(16);
private static Mesh _cubeMesh;
private static Material _material;
private static MaterialPropertyBlock _propertyBlock;
private static readonly int BaseColorPropId = Shader.PropertyToID("_BaseColor");
private static bool _initialized;
/// <summary>
/// Отрисовка хитбокса напрямую по BoxCollider
/// </summary>
public static void DrawBox(BoxCollider collider, Color color, float duration = 1.0f)
{
if (collider == null) return;
Transform t = collider.transform;
Vector3 worldCenter = t.TransformPoint(collider.center);
Vector3 worldSize = Vector3.Scale(collider.size, t.lossyScale);
DrawBox(worldCenter, worldSize, t.rotation, color, duration);
}
/// <summary>
/// Отрисовка хитбокса по параметрам позиции, габаритов и вращения
/// </summary>
public static void DrawBox(Vector3 center, Vector3 size, Quaternion rotation, Color color,
float duration = 1.0f)
{
EnsureInitialized();
if (_material == null) return;
_boxes.Add(new ActiveBox
{
Matrix = Matrix4x4.TRS(center, rotation, size),
Color = color,
ExpireTime = Time.unscaledTime + duration
});
}
/// <summary>
/// Принудительная очистка всех активных хитбоксов
/// </summary>
public static void Clear()
{
_boxes.Clear();
}
private static void EnsureInitialized()
{
if (_initialized) return;
_cubeMesh = GenerateUnitCubeMesh();
_propertyBlock = new MaterialPropertyBlock();
var shader = Shader.Find("Custom/HitboxVisualizer");
if (shader != null)
{
_material = new Material(shader) { hideFlags = HideFlags.HideAndDontSave };
}
else
{
Debug.LogError("[HitboxDrawer] Не найден шейдер 'Custom/HitboxVisualizer'!");
return;
}
RenderPipelineManager.beginContextRendering += OnRender;
Camera.onPreCull += OnPreCullLegacy;
_initialized = true;
}
private static void OnRender(ScriptableRenderContext ctx, List<Camera> cameras) => RenderBoxes();
private static void OnPreCullLegacy(Camera cam)
{
if (GraphicsSettings.currentRenderPipeline == null) RenderBoxes();
}
private static void RenderBoxes()
{
int count = _boxes.Count;
if (count == 0) return;
float now = Time.unscaledTime;
for (int i = count - 1; i >= 0; i--)
{
ActiveBox box = _boxes[i];
if (now >= box.ExpireTime)
{
// Быстрое удаление за O(1)
_boxes[i] = _boxes[_boxes.Count - 1];
_boxes.RemoveAt(_boxes.Count - 1);
continue;
}
_propertyBlock.SetColor(BaseColorPropId, box.Color);
Graphics.DrawMesh(
_cubeMesh,
box.Matrix,
_material,
0,
null,
0,
_propertyBlock,
castShadows: false,
receiveShadows: false,
useLightProbes: false
);
}
}
private static Mesh GenerateUnitCubeMesh()
{
Mesh mesh = new Mesh { name = "Hitbox_UnitCube" };
// 24 вершины с уникальными UV на каждой грани для точной границы
Vector3[] vertices = new Vector3[24]
{
// Передняя (+Z)
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),
// Задняя (-Z)
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),
// Правая (+X)
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),
// Левая (-X)
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),
// Верхняя (+Y)
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),
// Нижняя (-Y)
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)
};
Vector2[] uvs = new Vector2[24];
int[] triangles = new int[36];
for (int face = 0; face < 6; face++)
{
int v = face * 4;
uvs[v + 0] = new Vector2(0, 0);
uvs[v + 1] = new Vector2(1, 0);
uvs[v + 2] = new Vector2(1, 1);
uvs[v + 3] = new Vector2(0, 1);
int t = face * 6;
triangles[t + 0] = v + 0;
triangles[t + 1] = v + 2;
triangles[t + 2] = v + 1;
triangles[t + 3] = v + 0;
triangles[t + 4] = v + 3;
triangles[t + 5] = v + 2;
}
mesh.vertices = vertices;
mesh.uv = uvs;
mesh.triangles = triangles;
mesh.RecalculateBounds();
return mesh;
}
}
}