diff --git a/Assets/Editor/CameraHUDFixer.cs b/Assets/Editor/CameraHUDFixer.cs new file mode 100644 index 0000000..c7950af --- /dev/null +++ b/Assets/Editor/CameraHUDFixer.cs @@ -0,0 +1,27 @@ +using UnityEngine; +using UnityEditor; +using UnityEditor.SceneManagement; + +// Tools -> Fix Camera & HUD: один раз исправить сцену и сохранить. +// - HUD_Canvas -> Screen Space Overlay, отвязан от камеры +// - с камеры убраны CameraZoom / CameraController / FlyCamera, добавлена CityCamera +// - удалены компоненты с "Missing script" (скрипты, которых в проекте уже нет) +public static class CameraHUDFixer +{ + // [MenuItem("Tools/Fix Camera & HUD")] -- отключено, см. RestoreOldCamera + public static void Fix() + { + int missing = 0; + var hud = GameObject.Find("HUD_Canvas"); + if (hud != null) missing += GameObjectUtility.RemoveMonoBehavioursWithMissingScript(hud); + if (Camera.main != null) missing += GameObjectUtility.RemoveMonoBehavioursWithMissingScript(Camera.main.gameObject); + + int changes = HUDCameraSetup.Apply(true); + + if (hud != null) EditorSceneManager.MarkSceneDirty(hud.scene); + else if (Camera.main != null) EditorSceneManager.MarkSceneDirty(Camera.main.gameObject.scene); + + Debug.Log("Камера и HUD исправлены: изменений " + changes + ", удалено пустых скриптов " + missing + + ". Сохрани сцену (Ctrl+S). Границы карты камеры: компонент CityCamera на Main Camera."); + } +} diff --git a/Assets/Editor/CameraHUDFixer.cs.meta b/Assets/Editor/CameraHUDFixer.cs.meta new file mode 100644 index 0000000..3363b1b --- /dev/null +++ b/Assets/Editor/CameraHUDFixer.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 43380bdc85562cc4abb95b707e06cfb6 \ No newline at end of file diff --git a/Assets/Editor/HUDPanelsBuilder.cs b/Assets/Editor/HUDPanelsBuilder.cs new file mode 100644 index 0000000..07253f6 --- /dev/null +++ b/Assets/Editor/HUDPanelsBuilder.cs @@ -0,0 +1,337 @@ +using System; +using UnityEngine; +using UnityEngine.UI; +using UnityEditor; +using UnityEditor.Events; +using UnityEditor.SceneManagement; +using TMPro; + +// Кладётся в Assets/Editor/. +// Tools -> Build HUD Panels: +// 1) находит HUD_Canvas, +// 2) создаёт для каждой кнопки свою панель (заголовок + крестик + область Content), +// 3) вешает на панель её скрипт (QuestsPanel, ShopPanel...) -- он сам рисует содержимое в игре, +// 4) вешает UIPanelManager и подключает onClick ВСЕХ кнопок по их именам, +// 5) добавляет красные значки (HUDBadge) на кнопки, где бывает "что забрать". +// Можно запускать повторно -- старые панели пересоздаются. +public class HUDPanelsBuilder +{ + class Entry + { + public string button, id, title; public Type panel; public bool badge; + public Entry(string button, string id, string title, Type panel, bool badge) + { this.button = button; this.id = id; this.title = title; this.panel = panel; this.badge = badge; } + } + + // имя кнопки в иерархии -> id панели, заголовок, скрипт панели, нужен ли значок + static readonly Entry[] Map = + { + new Entry("RightBtn_Quests", "Quests", "ЗАДАНИЯ", typeof(QuestsPanel), true), + new Entry("RightBtn_Events", "Events", "СОБЫТИЯ", typeof(EventsPanel), true), + new Entry("RightBtn_Rewards", "Rewards", "НАГРАДЫ", typeof(RewardsPanel), true), + new Entry("NavBtn_Mail", "Mail", "ПОЧТА", typeof(MailPanel), true), + new Entry("SettingsButton", "Settings", "НАСТРОЙКИ", typeof(SettingsPanel), false), + new Entry("PlayerAvatar", "Profile", "ПРОФИЛЬ", typeof(ProfilePanel), false), + new Entry("Nav_Hero", "Hero", "ГЕРОЙ", typeof(HeroPanel), true), + new Entry("Nav_Battle", "Battle", "БОЙ", typeof(BattlePanel), false), + new Entry("Nav_Arena", "Arena", "АРЕНА", typeof(ArenaPanel), true), + new Entry("Nav_Troops", "Troops", "ВОЙСКА", typeof(TroopsPanel), false), + new Entry("Nav_Shop", "Shop", "МАГАЗИН", typeof(ShopPanel), true), + new Entry("Nav_World", "World", "КАРТА МИРА", typeof(WorldMapPanel), true), // кнопки нет -- билдер создаст + new Entry("Nav_Home", "Home", null, null, false), // без панели: закрывает всё + }; + + [MenuItem("Tools/Build HUD Panels")] + public static void Build() + { + GameObject canvasGO = GameObject.Find("HUD_Canvas"); + if (canvasGO == null) { Debug.LogError("Не найден объект HUD_Canvas на сцене."); return; } + + + // казармы: префаб + данные здания + в список стройки + EnsureBarracks(); + + // менеджер + UIPanelManager manager = canvasGO.GetComponent(); + if (manager == null) manager = canvasGO.AddComponent(); + manager.panels.Clear(); + + // корень для панелей (пересоздаём) + Transform old = canvasGO.transform.Find("Panels"); + if (old != null) UnityEngine.Object.DestroyImmediate(old.gameObject); + GameObject root = new GameObject("Panels", typeof(RectTransform)); + root.transform.SetParent(canvasGO.transform, false); + Stretch(root.GetComponent(), Vector4.zero); + + // Корень сам не блокирует клики (нет Image), поэтому нижняя/верхняя панели HUD остаются живыми. + int wired = 0; + string missing = ""; + foreach (var m in Map) + { + if (m.title != null) + { + // карте мира нужно больше места -- её окно почти на весь экран между верхней и нижней панелями + Vector4 offsets = m.id == "World" ? new Vector4(16, 145, 16, 195) : new Vector4(30, 250, 30, 230); + GameObject panel = CreatePanel(root.transform, m.id, m.title, manager, offsets); + if (m.panel != null) panel.AddComponent(m.panel); + manager.panels.Add(new UIPanelManager.PanelEntry { id = m.id, panel = panel }); + } + + Button btn = FindButton(canvasGO.transform, m.button); + if (btn == null && m.id == "World") btn = CreateWorldButton(canvasGO.transform, root.transform); + if (btn == null) { Debug.LogWarning("Кнопка не найдена: " + m.button); missing += " " + m.button; continue; } + + // убираем старые подписки из Inspector и вешаем свою + for (int i = btn.onClick.GetPersistentEventCount() - 1; i >= 0; i--) + UnityEventTools.RemovePersistentListener(btn.onClick, i); + UnityEventTools.AddStringPersistentListener(btn.onClick, manager.Open, m.id); + + // красный значок "есть что забрать" + HUDBadge badge = btn.GetComponent(); + if (m.badge) + { + if (badge == null) badge = btn.gameObject.AddComponent(); + badge.panelId = m.id; + } + else if (badge != null) UnityEngine.Object.DestroyImmediate(badge); + + wired++; + } + + // Кнопка "СТРОЙКА": включает/выключает режим стройки (тап по участку -> выбор здания). + // Старое меню стройки прячем: при запуске игры оно больше не открывается. + GameObject buildMenu = null; + foreach (var t in canvasGO.GetComponentsInChildren(true)) + if (t.name == "BuildMenuPanel") { buildMenu = t.gameObject; break; } + if (buildMenu != null) buildMenu.SetActive(false); + + BuildModeController buildMode = canvasGO.GetComponent(); + if (buildMode == null) buildMode = canvasGO.AddComponent(); + Button buildBtn = FindButton(canvasGO.transform, "Nav_Build"); + if (buildBtn == null) + buildBtn = CreateSideButton(canvasGO.transform, root.transform, "Nav_Build", "СТРОЙКА", + "Assets/UI/HUD v2/Рюкзак/v1.png", new Vector2(30, 400), new Color(0.55f, 0.4f, 0.2f)); + for (int i = buildBtn.onClick.GetPersistentEventCount() - 1; i >= 0; i--) + UnityEventTools.RemovePersistentListener(buildBtn.onClick, i); + UnityEventTools.AddVoidPersistentListener(buildBtn.onClick, buildMode.ToggleMode); + buildMode.modeButton = buildBtn; + buildMode.legacyBuildMenu = buildMenu; + EditorUtility.SetDirty(buildMode); + + // Меню стройки растянуто на весь экран и закрывало все кнопки HUD (ловило клики и белило экран). + // Делаем его фон прозрачным и "сквозным" -- кнопки зданий внутри продолжают работать. + foreach (string see in new[] { "BuildMenuPanel", "ButtonContainer" }) + { + foreach (var t in canvasGO.GetComponentsInChildren(true)) + { + if (t.name != see) continue; + var img = t.GetComponent(); + if (img == null) continue; + var c = img.color; c.a = 0f; img.color = c; + img.raycastTarget = false; + EditorUtility.SetDirty(img); + } + } + + EditorUtility.SetDirty(manager); + EditorSceneManager.MarkSceneDirty(canvasGO.scene); + Debug.Log("Готово: панелей " + manager.panels.Count + ", подключено кнопок " + wired + " из " + Map.Length + + (missing.Length > 0 ? ". Не найдены:" + missing : "") + ". Не забудь сохранить сцену (Ctrl+S)."); + } + + // ===================== КАЗАРМЫ ===================== + + const string BarracksPrefabPath = "Assets/Prefab/Buildings/Barracks.prefab"; + const string BarracksDataPath = "Assets/Prefab/Buildings/Data_Barracks.asset"; + + [MenuItem("Tools/Create Barracks")] + public static void EnsureBarracks() + { + Sprite icon = LoadSprite("Assets/UI/HUD v2/Войска/v1.png"); + GameObject prefab = AssetDatabase.LoadAssetAtPath(BarracksPrefabPath); + + if (prefab == null) + { + // берём готовое здание как шаблон: тот же масштаб, слой, материал, коллайдер + string template = "Assets/Prefab/Buildings/Food.prefab"; + if (AssetDatabase.LoadAssetAtPath(template) == null) + { + var bmT = UnityEngine.Object.FindAnyObjectByType(); + if (bmT != null) + foreach (var b in bmT.allBuildings) + if (b != null && b.prefab != null) { template = AssetDatabase.GetAssetPath(b.prefab); break; } + } + if (!AssetDatabase.CopyAsset(template, BarracksPrefabPath)) + { + Debug.LogError("Не удалось создать префаб казарм из " + template); + return; + } + + GameObject root = PrefabUtility.LoadPrefabContents(BarracksPrefabPath); + root.name = "Barracks"; + foreach (var c in root.GetComponents()) UnityEngine.Object.DestroyImmediate(c); + foreach (var c in root.GetComponents()) UnityEngine.Object.DestroyImmediate(c); + foreach (var c in root.GetComponents()) UnityEngine.Object.DestroyImmediate(c); + var kids = new System.Collections.Generic.List(); + foreach (Transform ch in root.transform) kids.Add(ch.gameObject); // полоска запаса ресурса -- не нужна + foreach (var k in kids) UnityEngine.Object.DestroyImmediate(k); + + var sr = root.GetComponent(); + if (sr != null) + { + if (icon != null) + { + Vector2 oldSize = sr.sprite != null ? (Vector2)sr.sprite.bounds.size : Vector2.one; + sr.sprite = icon; + sr.color = Color.white; + float k = Mathf.Min(oldSize.x / icon.bounds.size.x, oldSize.y / icon.bounds.size.y); + root.transform.localScale = Vector3.Scale(root.transform.localScale, new Vector3(k, k, 1)); + } + else sr.color = new Color(1f, 0.6f, 0.6f); // нет иконки -- хотя бы отличаем цветом + + var box = root.GetComponent(); + if (box != null && sr.sprite != null) { box.size = sr.sprite.bounds.size; box.offset = sr.sprite.bounds.center; } + } + if (root.GetComponent() == null) root.AddComponent(); + PrefabUtility.SaveAsPrefabAsset(root, BarracksPrefabPath); + PrefabUtility.UnloadPrefabContents(root); + prefab = AssetDatabase.LoadAssetAtPath(BarracksPrefabPath); + Debug.Log("Создан префаб казарм: " + BarracksPrefabPath); + } + + BuildingData data = AssetDatabase.LoadAssetAtPath(BarracksDataPath); + if (data == null) + { + data = ScriptableObject.CreateInstance(); + data.buildingName = "Казармы"; + data.icon = icon; + data.prefab = prefab; + data.requiredTownHallLevel = 1; + data.cost = new[] + { + new ResourceCost { type = ResourceType.Wood, amount = 300 }, + new ResourceCost { type = ResourceType.Stone, amount = 200 }, + }; + AssetDatabase.CreateAsset(data, BarracksDataPath); + AssetDatabase.SaveAssets(); + Debug.Log("Созданы данные здания: " + BarracksDataPath); + } + + var bm = UnityEngine.Object.FindAnyObjectByType(); + if (bm != null && !bm.allBuildings.Contains(data)) + { + bm.allBuildings.Add(data); + EditorUtility.SetDirty(bm); + EditorSceneManager.MarkSceneDirty(bm.gameObject.scene); + Debug.Log("Казармы добавлены в список стройки (BuildManager)"); + } + } + + static Sprite LoadSprite(string path) + { + foreach (var a in AssetDatabase.LoadAllAssetsAtPath(path)) + if (a is Sprite) return (Sprite)a; + return null; + } + + // ищем объект по имени среди всех детей (включая выключенные); Button может быть на нём или внутри + static Button FindButton(Transform root, string name) + { + foreach (var t in root.GetComponentsInChildren(true)) + { + if (t.name != name) continue; + Button b = t.GetComponent