178 lines
6.1 KiB
C#
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;
|
|
}
|
|
}
|
|
} |