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", null, null, false), // кнопка "МИР" -> сцена большой карты (UIPanelManager.Open) 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