using UnityEngine; using UnityEngine.UI; // Отвязывает HUD от камеры и ставит одну нормальную камеру. // // Было: HUD_Canvas в режиме World Space был дочерним объектом камеры, а CameraZoom масштабировал // саму камеру, чтобы HUD не уменьшался при зуме. Поэтому HUD ехал, дрожал и масштабировался криво. // Стало: HUD_Canvas -- Screen Space Overlay (всегда приклеен к экрану, камера на него не влияет), // на камере -- только CityCamera (перетаскивание, щипок, колёсико, границы карты). // // Работает в двух местах: // - при запуске игры (автоматически), если сцену ещё не исправили в редакторе; // - Tools -> Fix Camera & HUD (или Build HUD Panels) -- исправляет и сохраняет сцену навсегда. public static class HUDCameraSetup { // ОТКЛЮЧЕНО по просьбе команды: камера и HUD вернулись к прежней схеме (Tools -> Restore Old Camera & HUD). // Автоисправление при запуске больше не выполняется. static void AutoFix() { // и при каждой следующей загрузке города (возврат с карты мира) UnityEngine.SceneManagement.SceneManager.sceneLoaded += (s, m) => Apply(false); int changes = Apply(false); if (changes > 0) Debug.Log("[КАМЕРА/HUD] Исправлено при запуске: " + changes + ". Чтобы сохранить в сцене навсегда -- Tools -> Fix Camera & HUD"); } // editMode = true -- вызывается из редактора: лишние компоненты удаляются, а не выключаются public static int Apply(bool editMode) { int changes = 0; // ---------- HUD ---------- var hudGO = GameObject.Find("HUD_Canvas"); if (hudGO != null) { var canvas = hudGO.GetComponent(); var rt = (RectTransform)hudGO.transform; if (canvas != null && canvas.renderMode != RenderMode.ScreenSpaceOverlay) { Vector2 designSize = rt.sizeDelta; // размер, под который дизайнер расставил HUD if (designSize.x < 100 || designSize.y < 100) designSize = new Vector2(1080, 1920); if (hudGO.transform.parent != null) hudGO.transform.SetParent(null, false); canvas.renderMode = RenderMode.ScreenSpaceOverlay; canvas.sortingOrder = 100; rt.localScale = Vector3.one; rt.localRotation = Quaternion.identity; var scaler = hudGO.GetComponent(); if (scaler == null) scaler = hudGO.AddComponent(); scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; scaler.referenceResolution = designSize; // HUD выглядит так же, как его собрал дизайнер scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight; scaler.matchWidthOrHeight = 1f; // как было в мире: высота HUD = высота экрана changes++; } // высота HUD = высота экрана (так HUD выглядел, пока был привязан к камере) var sc = hudGO.GetComponent(); if (sc != null && canvas != null && canvas.renderMode == RenderMode.ScreenSpaceOverlay && sc.matchWidthOrHeight != 1f) { sc.matchWidthOrHeight = 1f; changes++; } changes += ConvertSpritesToImages(hudGO, editMode); changes += RemoveOrDisable(hudGO, "HUDWorldSpaceSync", editMode); } // ---------- КАМЕРА ---------- var cam = Camera.main; if (cam != null) { changes += RemoveOrDisable(cam.gameObject, "CameraZoom", editMode); changes += RemoveOrDisable(cam.gameObject, "CameraController", editMode); changes += RemoveOrDisable(cam.gameObject, "FlyCamera", editMode); if (cam.transform.localScale != Vector3.one) { cam.transform.localScale = Vector3.one; changes++; } if (cam.transform.rotation != Quaternion.identity) { cam.transform.rotation = Quaternion.identity; changes++; } if (cam.GetComponent() == null) { cam.gameObject.AddComponent(); changes++; } } return changes; } // В экранном Canvas обычные SpriteRenderer не рисуются как часть интерфейса -- они улетают в мир // огромными картинками (отсюда белая "клетка" на весь экран). Переделываем их в UI Image того же размера. static int ConvertSpritesToImages(GameObject hud, bool editMode) { int n = 0; foreach (var sr in hud.GetComponentsInChildren(true)) { if (sr.sprite == null) { sr.enabled = false; continue; } var t = sr.transform; var parent = t.parent as RectTransform; if (parent == null) { sr.enabled = false; continue; } var go = new GameObject(t.name + "_UI", typeof(RectTransform), typeof(Image)); var rt = (RectTransform)go.transform; rt.SetParent(parent, false); rt.SetSiblingIndex(t.GetSiblingIndex()); rt.anchorMin = rt.anchorMax = parent.pivot; // localPosition считается от pivot родителя rt.pivot = new Vector2(0.5f, 0.5f); Vector2 size = Vector2.Scale(sr.sprite.bounds.size, new Vector2(Mathf.Abs(t.localScale.x), Mathf.Abs(t.localScale.y))); rt.sizeDelta = size; rt.localPosition = t.localPosition + (Vector3)Vector2.Scale(sr.sprite.bounds.center, t.localScale); rt.localRotation = t.localRotation; var img = go.GetComponent(); img.sprite = sr.sprite; img.color = sr.color; img.raycastTarget = false; img.preserveAspect = false; if (editMode) Object.DestroyImmediate(t.gameObject); else t.gameObject.SetActive(false); n++; } return n; } // Компоненты ищем по имени класса: часть из них (FlyCamera, HUDWorldSpaceSync) в проекте уже нет static int RemoveOrDisable(GameObject go, string typeName, bool editMode) { int n = 0; foreach (var mb in go.GetComponents()) { if (mb == null || mb.GetType().Name != typeName) continue; if (editMode) Object.DestroyImmediate(mb); else if (mb.enabled) mb.enabled = false; else continue; n++; } return n; } }