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 _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; /// /// Отрисовка хитбокса напрямую по BoxCollider /// 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); } /// /// Отрисовка хитбокса по параметрам позиции, габаритов и вращения /// 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 }); } /// /// Принудительная очистка всех активных хитбоксов /// 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 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; } } }