using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; // 3D НА ПРИМИТИВАХ: общие помощники для карты мира и 3D-поселения (переход на 2.5D, 04.11.2026). // Материал берётся у встроенного примитива (шейдер URP по умолчанию точно есть в сборке), цвета кешируются. // Подписи (TextMeshPro) поворачиваются к камере (FaceCamera). public static class Map3D { static Material baseMaterial; static readonly Dictionary materials = new Dictionary(); public static Material Mat(Color color) { if (baseMaterial == null) { var probe = GameObject.CreatePrimitive(PrimitiveType.Cube); baseMaterial = new Material(probe.GetComponent().sharedMaterial); Object.Destroy(probe); materials.Clear(); } if (!materials.TryGetValue(color, out var m) || m == null) { m = new Material(baseMaterial) { color = color }; materials[color] = m; } return m; } // Объект на земле: основание size.x * size.y (по X и Z), высота height, низ -- на уровне ground.y public static Renderer Block(Transform parent, string name, PrimitiveType type, Color color, Vector3 ground, Vector2 size, float height, bool collider = false) { var go = GameObject.CreatePrimitive(type); go.name = name; if (!collider) Object.Destroy(go.GetComponent()); go.transform.SetParent(parent, false); // У цилиндра высота -- 2 единицы при масштабе 1 float scaleY = type == PrimitiveType.Cylinder || type == PrimitiveType.Capsule ? height / 2f : height; go.transform.localScale = new Vector3(size.x, scaleY, size.y); go.transform.position = ground + Vector3.up * height / 2f; var r = go.GetComponent(); r.sharedMaterial = Mat(color); return r; } // Подпись над объектом, всегда лицом к камере public static TextMeshPro Label(Transform parent, string text, Vector3 position, float fontSize) { var go = new GameObject("Label"); go.transform.SetParent(parent, false); go.transform.position = position; var tmp = go.AddComponent(); tmp.text = text; tmp.fontSize = fontSize; tmp.alignment = TextAlignmentOptions.Center; tmp.rectTransform.sizeDelta = new Vector2(6f, 1.6f); tmp.color = Color.white; tmp.outlineWidth = 0.25f; tmp.outlineColor = Color.black; go.AddComponent(); return tmp; } // Камере -- обычный 3D-рендерер URP (второй в списке; добавляет Tools -> Bogatyri -> 3D-город). // Нет его -- остаётся рендерер по умолчанию, и в лог пишется, что делать. public static void UseRenderer3D(Camera cam) { var pipeline = GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset; var data = cam != null ? cam.GetUniversalAdditionalCameraData() : null; if (pipeline == null || data == null) return; var list = pipeline.rendererDataList; for (int i = 0; i < list.Length; i++) if (list[i] is UniversalRendererData) { data.SetRenderer(i); return; } Debug.LogWarning("[3D] В URP нет обычного 3D-рендерера: выполните Tools -> Bogatyri -> 3D-город: создать сцену City3D (он добавится). Без него 3D рисуется без света."); } // Солнце и общий свет, если в сцене их нет (2D-сцены света не имели) public static void EnsureSun() { foreach (var l in Object.FindObjectsByType(FindObjectsSortMode.None)) if (l.type == LightType.Directional) return; var sunGo = new GameObject("Sun"); var sun = sunGo.AddComponent(); sun.type = LightType.Directional; sun.intensity = 1.15f; sun.shadows = LightShadows.Soft; sunGo.transform.rotation = Quaternion.Euler(50f, -30f, 0f); RenderSettings.ambientMode = AmbientMode.Flat; RenderSettings.ambientLight = new Color(0.55f, 0.58f, 0.62f); } } // Подпись поворачивается к камере (наклонный вид 2.5D) public class FaceCamera : MonoBehaviour { static Camera cam; void LateUpdate() { if (cam == null) cam = Camera.main; if (cam != null) transform.rotation = cam.transform.rotation; } }