Модуль Чата (Bogatyri.Chat, без Unity): - чат вынесен из Social в отдельный модуль; контракт IChat в ядре, событие ChatMessageReceivedEvent, LocalPlayer (id и имя игрока) - панель чата работает только через контракт и события - разворот чата на весь экран: кнопка «Развернуть/Свернуть» или Esc Модуль Круга (Bogatyri.Circle, без Unity): - основать Круг, вступить, выйти; роли главы и участника; 30 мест - уход главы передаёт главенство, последний участник распускает Круг - секция сохранения "circle", участвует в сбросе сохранений - окно «Круг» (кнопка внизу карты мира), демо-Круги до появления сервера - канал «Круг» в чате — только для участников Круга Прочее: - кнопка «Почта» привязана к правому краю нижней панели (в безопасной зоне она уезжала за экран) - тесты Чата и Круга; документы Chat.md, Circle.md, обновлены README, Core, Save Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
527 lines
26 KiB
C#
527 lines
26 KiB
C#
using System.Collections.Generic;
|
||
using System.IO;
|
||
using TMPro;
|
||
using UnityEditor;
|
||
using UnityEditor.SceneManagement;
|
||
using UnityEngine;
|
||
using UnityEngine.EventSystems;
|
||
using UnityEngine.UI;
|
||
|
||
// Tools -> Build World Map Scene
|
||
// 1. Создаёт сцену Assets/Scenes/WorldMap.unity: камера, EventSystem, UI карты, WorldMapController.
|
||
// 2. Ставит MVP1 и WorldMap в Build Settings (иначе SceneManager.LoadScene их не найдёт).
|
||
// 3. Проверяет, что на кнопке Nav_Battle в городе висит OpenBattleButton.
|
||
// Можно запускать повторно -- сцена карты пересоздаётся заново.
|
||
public static class WorldMapSceneBuilder
|
||
{
|
||
const string WorldMapScenePath = "Assets/Scenes/WorldMap.unity";
|
||
const string CityScenePath = "Assets/Scenes/MVP1.unity";
|
||
|
||
[MenuItem("Tools/Build World Map Scene")]
|
||
public static void Build()
|
||
{
|
||
if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) return;
|
||
|
||
CreateWorldMapScene();
|
||
UpdateBuildSettings();
|
||
HookUpCityBattleButton();
|
||
|
||
Debug.Log("[КАРТА] Сцена карты мира создана, кнопка БОЙ в городе подключена. Запускай MVP1 и жми БОЙ.");
|
||
}
|
||
|
||
// ---------------- СЦЕНА КАРТЫ ----------------
|
||
|
||
static void CreateWorldMapScene()
|
||
{
|
||
var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
||
|
||
var camGO = new GameObject("Main Camera", typeof(Camera), typeof(MapCameraController));
|
||
camGO.tag = "MainCamera";
|
||
camGO.transform.position = new Vector3(0, 0, -10);
|
||
Camera cam = camGO.GetComponent<Camera>();
|
||
cam.orthographic = true;
|
||
cam.orthographicSize = 8f;
|
||
cam.clearFlags = CameraClearFlags.SolidColor;
|
||
cam.backgroundColor = new Color(0.12f, 0.16f, 0.12f);
|
||
|
||
new GameObject("EventSystem", typeof(EventSystem), typeof(StandaloneInputModule));
|
||
|
||
var canvasGO = new GameObject("WorldMap_Canvas", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
|
||
canvasGO.GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
|
||
CanvasScaler scaler = canvasGO.GetComponent<CanvasScaler>();
|
||
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
||
scaler.referenceResolution = new Vector2(1920, 1080);
|
||
scaler.matchWidthOrHeight = 0.5f;
|
||
RectTransform canvasRect = canvasGO.GetComponent<RectTransform>();
|
||
|
||
var controller = new GameObject("WorldMapController").AddComponent<WorldMapController>();
|
||
controller.mapCamera = camGO.GetComponent<MapCameraController>();
|
||
|
||
// ---- Верхняя панель: назад | войско | мой город ----
|
||
RectTransform topBar = CreatePanel(canvasRect, "TopBar", new Color(0.08f, 0.08f, 0.1f, 0.85f));
|
||
AnchorStretch(topBar, top: true, height: 110);
|
||
|
||
controller.soldiersText = CreateText(topBar, "SoldiersText", "Войска дома: 30", 40);
|
||
|
||
RectTransform back = CreateButton(topBar, "BackButton", "НАЗАД", new Color(0.3f, 0.3f, 0.33f), out _);
|
||
AnchorSide(back, left: true, new Vector2(220, 80), 20);
|
||
controller.backButton = back.GetComponent<Button>();
|
||
|
||
RectTransform myCity = CreateButton(topBar, "MyCityButton", "МОЙ ГОРОД", new Color(0.25f, 0.45f, 0.8f), out _);
|
||
AnchorSide(myCity, left: false, new Vector2(280, 80), 20);
|
||
controller.myCityButton = myCity.GetComponent<Button>();
|
||
|
||
// ---- Подсказка (над нижней панелью, правее чата) ----
|
||
RectTransform hintBar = CreatePanel(canvasRect, "HintBar", new Color(0.08f, 0.08f, 0.1f, 0.75f));
|
||
SetRect(hintBar, new Vector2(0, 0), new Vector2(1, 0), new Vector2(0.5f, 0), Vector2.zero, Vector2.zero);
|
||
hintBar.offsetMin = new Vector2(ChatWidth + 24, 172);
|
||
hintBar.offsetMax = new Vector2(-12, 222);
|
||
controller.hintText = CreateText(hintBar, "Hint", "", 28);
|
||
|
||
// ---- Чат слева внизу ----
|
||
BuildChat(canvasRect);
|
||
|
||
// ---- Нижняя панель: Карта мира | События | Круг | Поселение ----
|
||
BuildNavBar(canvasRect, controller);
|
||
|
||
// ---- Кнопка "Рейтинг" слева ----
|
||
RectTransform ratingBtn = CreateButton(canvasRect, "RatingButton", "Рейтинг", new Color(0.12f, 0.2f, 0.4f, 0.95f), out _);
|
||
SetRect(ratingBtn, new Vector2(0, 1), new Vector2(0, 1), new Vector2(0, 1), new Vector2(12, -122), new Vector2(210, 110));
|
||
controller.ratingButton = ratingBtn.GetComponent<Button>();
|
||
|
||
// ---- Панель рейтинга (выше всего, кроме окна атаки и уведомлений) ----
|
||
controller.ratingPanel = BuildRatingPanel(canvasRect);
|
||
|
||
// ---- Окно атаки ----
|
||
RectTransform attack = CreateCenterPanel(canvasRect, "AttackPanel", new Vector2(900, 640));
|
||
controller.attackPanel = attack.gameObject;
|
||
|
||
RectTransform attackTitle = CreateTextRect(attack, "Title", "Замок", 56);
|
||
AnchorTop(attackTitle, 110, 20);
|
||
controller.attackTitleText = attackTitle.GetComponent<TMP_Text>();
|
||
|
||
RectTransform attackInfo = CreateTextRect(attack, "Info", "", 32);
|
||
AnchorTop(attackInfo, 170, 130);
|
||
controller.attackInfoText = attackInfo.GetComponent<TMP_Text>();
|
||
|
||
RectTransform sendCount = CreateTextRect(attack, "SoldiersToSend", "Отправить: 30 из 30", 36);
|
||
AnchorTop(sendCount, 60, 310);
|
||
controller.soldiersToSendText = sendCount.GetComponent<TMP_Text>();
|
||
|
||
GameObject sliderGO = DefaultControls.CreateSlider(new DefaultControls.Resources());
|
||
sliderGO.name = "SoldiersSlider";
|
||
RectTransform sliderRect = sliderGO.GetComponent<RectTransform>();
|
||
sliderRect.SetParent(attack, false);
|
||
AnchorTop(sliderRect, 50, 390);
|
||
sliderRect.offsetMin = new Vector2(80, sliderRect.offsetMin.y);
|
||
sliderRect.offsetMax = new Vector2(-80, sliderRect.offsetMax.y);
|
||
StyleSlider(sliderGO);
|
||
controller.soldiersSlider = sliderGO.GetComponent<Slider>();
|
||
|
||
RectTransform send = CreateButton(attack, "SendButton", "В ПОХОД", new Color(0.75f, 0.25f, 0.15f), out _);
|
||
AnchorBottom(send, new Vector2(320, 100), new Vector2(170, 30));
|
||
controller.sendButton = send.GetComponent<Button>();
|
||
|
||
RectTransform cancel = CreateButton(attack, "CancelButton", "ОТМЕНА", new Color(0.3f, 0.3f, 0.33f), out _);
|
||
AnchorBottom(cancel, new Vector2(320, 100), new Vector2(-170, 30));
|
||
controller.cancelButton = cancel.GetComponent<Button>();
|
||
|
||
attack.gameObject.SetActive(false);
|
||
|
||
// ---- Уведомление ----
|
||
RectTransform popup = CreateCenterPanel(canvasRect, "PopupPanel", new Vector2(860, 520));
|
||
controller.popupPanel = popup.gameObject;
|
||
|
||
RectTransform popupTitle = CreateTextRect(popup, "Title", "ПОБЕДА", 72);
|
||
AnchorTop(popupTitle, 120, 20);
|
||
controller.popupTitleText = popupTitle.GetComponent<TMP_Text>();
|
||
|
||
RectTransform popupBody = CreateTextRect(popup, "Body", "", 32);
|
||
popupBody.offsetMin = new Vector2(40, 140);
|
||
popupBody.offsetMax = new Vector2(-40, -140);
|
||
controller.popupBodyText = popupBody.GetComponent<TMP_Text>();
|
||
|
||
RectTransform ok = CreateButton(popup, "OkButton", "OK", new Color(0.2f, 0.5f, 0.25f), out _);
|
||
AnchorBottom(ok, new Vector2(300, 100), new Vector2(0, 30));
|
||
controller.popupOkButton = ok.GetComponent<Button>();
|
||
|
||
popup.gameObject.SetActive(false);
|
||
|
||
EditorSceneManager.SaveScene(scene, WorldMapScenePath);
|
||
}
|
||
|
||
// ---------------- ЧАТ ----------------
|
||
|
||
const float ChatWidth = 620f;
|
||
|
||
static void BuildChat(RectTransform canvasRect)
|
||
{
|
||
RectTransform panel = CreatePanel(canvasRect, "ChatPanel", new Color(0.05f, 0.06f, 0.09f, 0.9f));
|
||
SetRect(panel, Vector2.zero, Vector2.zero, Vector2.zero, new Vector2(12, 12), new Vector2(ChatWidth, 350));
|
||
ChatPanel chat = panel.gameObject.AddComponent<ChatPanel>();
|
||
|
||
RectTransform worldTab = CreateButton(panel, "WorldTab", "Общий мир", new Color(0.16f, 0.35f, 0.7f), out _);
|
||
SetRect(worldTab, new Vector2(0, 1), new Vector2(0, 1), new Vector2(0, 1), new Vector2(10, -10), new Vector2(220, 54));
|
||
chat.worldTabButton = worldTab.GetComponent<Button>();
|
||
|
||
RectTransform circleTab = CreateButton(panel, "CircleTab", "Круг", new Color(0.12f, 0.13f, 0.17f), out _);
|
||
SetRect(circleTab, new Vector2(0, 1), new Vector2(0, 1), new Vector2(0, 1), new Vector2(240, -10), new Vector2(220, 54));
|
||
chat.circleTabButton = circleTab.GetComponent<Button>();
|
||
|
||
RectTransform expand = CreateButton(panel, "ExpandButton", "Развернуть", new Color(0.12f, 0.13f, 0.17f), out TMP_Text expandText);
|
||
SetRect(expand, new Vector2(1, 1), new Vector2(1, 1), new Vector2(1, 1), new Vector2(-10, -10), new Vector2(150, 54));
|
||
expandText.fontSize = 24;
|
||
chat.expandButton = expand.GetComponent<Button>();
|
||
|
||
// Лента: текст прижат к низу, старые строки уходят вверх и обрезаются маской
|
||
var messagesGO = new GameObject("Messages", typeof(RectTransform), typeof(RectMask2D));
|
||
RectTransform messages = messagesGO.GetComponent<RectTransform>();
|
||
messages.SetParent(panel, false);
|
||
messages.anchorMin = Vector2.zero;
|
||
messages.anchorMax = Vector2.one;
|
||
messages.offsetMin = new Vector2(14, 86);
|
||
messages.offsetMax = new Vector2(-14, -74);
|
||
|
||
RectTransform messagesText = CreateTextRect(messages, "MessagesText", "", 24, TextAlignmentOptions.BottomLeft);
|
||
TMP_Text tmp = messagesText.GetComponent<TMP_Text>();
|
||
tmp.overflowMode = TextOverflowModes.Overflow;
|
||
chat.messagesText = tmp;
|
||
|
||
// Поле ввода
|
||
GameObject inputGO = TMP_DefaultControls.CreateInputField(new TMP_DefaultControls.Resources());
|
||
inputGO.name = "InputField";
|
||
RectTransform inputRect = inputGO.GetComponent<RectTransform>();
|
||
inputRect.SetParent(panel, false);
|
||
SetRect(inputRect, Vector2.zero, new Vector2(1, 0), new Vector2(0.5f, 0), Vector2.zero, Vector2.zero);
|
||
inputRect.offsetMin = new Vector2(12, 12);
|
||
inputRect.offsetMax = new Vector2(-124, 74);
|
||
inputGO.GetComponent<Image>().color = new Color(0.12f, 0.13f, 0.17f);
|
||
|
||
TMP_InputField input = inputGO.GetComponent<TMP_InputField>();
|
||
input.pointSize = 26;
|
||
input.textComponent.color = Color.white;
|
||
((TMP_Text)input.placeholder).text = "Введите сообщение...";
|
||
((TMP_Text)input.placeholder).color = new Color(1f, 1f, 1f, 0.35f);
|
||
chat.inputField = input;
|
||
|
||
RectTransform send = CreateButton(panel, "SendButton", ">", new Color(0.2f, 0.45f, 0.85f), out _);
|
||
SetRect(send, new Vector2(1, 0), new Vector2(1, 0), new Vector2(1, 0), new Vector2(-12, 12), new Vector2(100, 62));
|
||
chat.sendButton = send.GetComponent<Button>();
|
||
}
|
||
|
||
// ---------------- НИЖНЯЯ ПАНЕЛЬ ----------------
|
||
|
||
static void BuildNavBar(RectTransform canvasRect, WorldMapController controller)
|
||
{
|
||
RectTransform bar = CreatePanel(canvasRect, "BottomNavBar", new Color(0.06f, 0.07f, 0.1f, 0.92f));
|
||
SetRect(bar, Vector2.zero, new Vector2(1, 0), new Vector2(0.5f, 0), Vector2.zero, Vector2.zero);
|
||
bar.offsetMin = new Vector2(ChatWidth + 24, 12);
|
||
bar.offsetMax = new Vector2(-12, 162);
|
||
|
||
var layout = bar.gameObject.AddComponent<HorizontalLayoutGroup>();
|
||
layout.padding = new RectOffset(10, 10, 10, 10);
|
||
layout.spacing = 10;
|
||
layout.childForceExpandWidth = true;
|
||
layout.childForceExpandHeight = true;
|
||
layout.childControlWidth = true;
|
||
layout.childControlHeight = true;
|
||
|
||
Color navColor = new Color(0.14f, 0.16f, 0.22f);
|
||
controller.worldMapNavButton = CreateButton(bar, "Nav_WorldMap", "Карта мира", navColor, out _).GetComponent<Button>();
|
||
controller.eventsNavButton = CreateButton(bar, "Nav_Events", "События", navColor, out _).GetComponent<Button>();
|
||
controller.circleNavButton = CreateButton(bar, "Nav_Circle", "Круг", navColor, out _).GetComponent<Button>();
|
||
controller.settlementNavButton = CreateButton(bar, "Nav_Settlement", "Поселение", new Color(0.18f, 0.25f, 0.45f), out _).GetComponent<Button>();
|
||
}
|
||
|
||
// ---------------- РЕЙТИНГ ----------------
|
||
|
||
static readonly Color RatingBg = new Color(0.04f, 0.06f, 0.1f, 0.97f);
|
||
static readonly Color RatingGold = new Color(0.95f, 0.8f, 0.5f);
|
||
|
||
const float RatingTableLeft = 340f;
|
||
const float RatingTableRight = 25f;
|
||
|
||
static RatingPanel BuildRatingPanel(RectTransform canvasRect)
|
||
{
|
||
RectTransform panel = CreatePanel(canvasRect, "RatingPanel", RatingBg);
|
||
SetRect(panel, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(1640, 920));
|
||
RatingPanel rating = panel.gameObject.AddComponent<RatingPanel>();
|
||
|
||
// Заголовок-баннер
|
||
RectTransform banner = CreatePanel(panel, "TitleBanner", new Color(0.12f, 0.25f, 0.55f));
|
||
SetRect(banner, new Vector2(0.5f, 1), new Vector2(0.5f, 1), new Vector2(0.5f, 1), new Vector2(0, -15), new Vector2(640, 90));
|
||
CreateText(banner, "Title", "Рейтинг", 60).color = RatingGold;
|
||
|
||
RectTransform close = CreateButton(panel, "CloseButton", "X", new Color(0.35f, 0.15f, 0.15f), out _);
|
||
SetRect(close, new Vector2(1, 1), new Vector2(1, 1), new Vector2(1, 1), new Vector2(-15, -15), new Vector2(80, 80));
|
||
rating.closeButton = close.GetComponent<Button>();
|
||
|
||
// Разделы слева -- порядок совпадает с RatingCategory
|
||
string[] categories = { "Игроки", "Круги", "Мощь", "PvP", "Развитие", "Сезон" };
|
||
rating.categoryButtons = new Button[categories.Length];
|
||
for (int i = 0; i < categories.Length; i++)
|
||
{
|
||
RectTransform tab = CreateButton(panel, "Category_" + i, categories[i], new Color(0.1f, 0.12f, 0.17f), out _);
|
||
SetRect(tab, new Vector2(0, 1), new Vector2(0, 1), new Vector2(0, 1), new Vector2(25, -130 - i * 112), new Vector2(290, 100));
|
||
rating.categoryButtons[i] = tab.GetComponent<Button>();
|
||
}
|
||
|
||
// Таблица
|
||
var tableGO = new GameObject("Table", typeof(RectTransform));
|
||
RectTransform table = tableGO.GetComponent<RectTransform>();
|
||
table.SetParent(panel, false);
|
||
table.anchorMin = Vector2.zero;
|
||
table.anchorMax = Vector2.one;
|
||
table.offsetMin = new Vector2(RatingTableLeft, 110);
|
||
table.offsetMax = new Vector2(-RatingTableRight, -130);
|
||
|
||
RectTransform header = CreatePanel(table, "Header", new Color(0.08f, 0.1f, 0.15f));
|
||
SetRect(header, new Vector2(0, 1), new Vector2(1, 1), new Vector2(0.5f, 1), Vector2.zero, new Vector2(0, 50));
|
||
Color headerColor = new Color(0.8f, 0.82f, 0.88f);
|
||
PlaceInRow(CreateTextRect(header, "Place", "Место", 26), 0, 140, 50).GetComponent<TMP_Text>().color = headerColor;
|
||
TMP_Text nameHeader = PlaceInRow(CreateTextRect(header, "Name", "Игрок", 26, TextAlignmentOptions.Left), 225, 330, 50).GetComponent<TMP_Text>();
|
||
nameHeader.color = headerColor;
|
||
rating.nameColumnHeader = nameHeader;
|
||
TMP_Text circleHeader = PlaceInRow(CreateTextRect(header, "Circle", "Круг", 26, TextAlignmentOptions.Left), 625, 320, 50).GetComponent<TMP_Text>();
|
||
circleHeader.color = headerColor;
|
||
rating.circleColumnHeader = circleHeader;
|
||
PlaceInRow(CreateTextRect(header, "Power", "Мощь", 26), 950, 220, 50).GetComponent<TMP_Text>().color = headerColor;
|
||
|
||
rating.rows = new RatingRowView[RatingPanel.PageSize];
|
||
for (int i = 0; i < rating.rows.Length; i++)
|
||
{
|
||
rating.rows[i] = BuildRatingRow(table, i);
|
||
}
|
||
|
||
rating.emptyText = CreateText(table, "EmptyText", "Раздел в разработке", 40);
|
||
rating.emptyText.gameObject.SetActive(false);
|
||
|
||
RectTransform status = CreateTextRect(panel, "StatusText", "", 26, TextAlignmentOptions.Left);
|
||
SetRect(status, Vector2.zero, Vector2.zero, Vector2.zero, new Vector2(RatingTableLeft, 30), new Vector2(520, 60));
|
||
rating.statusText = status.GetComponent<TMP_Text>();
|
||
|
||
// Страницы: < 1 2 3 4 5 > -- по центру под таблицей (а не под всей панелью)
|
||
float tableCenterX = (RatingTableLeft - RatingTableRight) / 2f;
|
||
Color pageColor = new Color(0.1f, 0.12f, 0.17f);
|
||
|
||
RectTransform prev = CreateButton(panel, "PrevPage", "<", pageColor, out _);
|
||
SetRect(prev, new Vector2(0.5f, 0), new Vector2(0.5f, 0), new Vector2(0.5f, 0), new Vector2(tableCenterX - 240, 25), new Vector2(64, 64));
|
||
rating.prevPageButton = prev.GetComponent<Button>();
|
||
|
||
rating.pageButtons = new Button[5];
|
||
for (int i = 0; i < rating.pageButtons.Length; i++)
|
||
{
|
||
RectTransform page = CreateButton(panel, "Page_" + i, (i + 1).ToString(), pageColor, out _);
|
||
SetRect(page, new Vector2(0.5f, 0), new Vector2(0.5f, 0), new Vector2(0.5f, 0), new Vector2(tableCenterX - 150 + i * 75, 25), new Vector2(64, 64));
|
||
rating.pageButtons[i] = page.GetComponent<Button>();
|
||
}
|
||
|
||
RectTransform next = CreateButton(panel, "NextPage", ">", pageColor, out _);
|
||
SetRect(next, new Vector2(0.5f, 0), new Vector2(0.5f, 0), new Vector2(0.5f, 0), new Vector2(tableCenterX + 240, 25), new Vector2(64, 64));
|
||
rating.nextPageButton = next.GetComponent<Button>();
|
||
|
||
panel.gameObject.SetActive(false);
|
||
return rating;
|
||
}
|
||
|
||
static RatingRowView BuildRatingRow(RectTransform table, int index)
|
||
{
|
||
const float rowHeight = 54f;
|
||
var view = new RatingRowView();
|
||
|
||
RectTransform row = CreatePanel(table, "Row_" + (index + 1), new Color(0.07f, 0.09f, 0.13f));
|
||
SetRect(row, new Vector2(0, 1), new Vector2(1, 1), new Vector2(0.5f, 1), new Vector2(0, -56 - index * 58), new Vector2(0, rowHeight));
|
||
view.root = row.gameObject;
|
||
view.background = row.GetComponent<Image>();
|
||
|
||
RectTransform medal = CreatePanel(row, "Medal", Color.white);
|
||
PlaceInRow(medal, 45, 50, 50);
|
||
view.medal = medal.GetComponent<Image>();
|
||
view.placeText = PlaceInRow(CreateTextRect(row, "Place", "1", 30), 0, 140, rowHeight).GetComponent<TMP_Text>();
|
||
|
||
RectTransform avatar = CreatePanel(row, "Avatar", Color.gray);
|
||
PlaceInRow(avatar, 150, 48, 48);
|
||
view.avatar = avatar.GetComponent<Image>();
|
||
view.avatarLetter = CreateText(avatar, "Letter", "В", 28);
|
||
|
||
view.nameText = PlaceInRow(CreateTextRect(row, "Name", "Игрок", 30, TextAlignmentOptions.Left), 225, 330, rowHeight).GetComponent<TMP_Text>();
|
||
|
||
RectTransform circleBanner = CreatePanel(row, "CircleBanner", Color.blue);
|
||
PlaceInRow(circleBanner, 570, 36, 46);
|
||
view.circleBanner = circleBanner.GetComponent<Image>();
|
||
view.circleText = PlaceInRow(CreateTextRect(row, "Circle", "Круг", 28, TextAlignmentOptions.Left), 625, 320, rowHeight).GetComponent<TMP_Text>();
|
||
|
||
TMP_Text power = PlaceInRow(CreateTextRect(row, "Power", "0", 28), 950, 220, rowHeight).GetComponent<TMP_Text>();
|
||
power.color = RatingGold;
|
||
view.powerText = power;
|
||
|
||
RectTransform inspect = CreateButton(row, "InspectButton", "?", new Color(0.18f, 0.2f, 0.27f), out _);
|
||
PlaceInRow(inspect, 1190, 56, 44);
|
||
view.inspectButton = inspect.GetComponent<Button>();
|
||
|
||
return view;
|
||
}
|
||
|
||
// Элемент строки: прижат к левому краю строки с отступом x, по вертикали по центру
|
||
static RectTransform PlaceInRow(RectTransform rt, float x, float width, float height)
|
||
{
|
||
SetRect(rt, new Vector2(0, 0.5f), new Vector2(0, 0.5f), new Vector2(0, 0.5f), new Vector2(x, 0), new Vector2(width, height));
|
||
return rt;
|
||
}
|
||
|
||
static void SetRect(RectTransform rt, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 position, Vector2 size)
|
||
{
|
||
rt.anchorMin = anchorMin;
|
||
rt.anchorMax = anchorMax;
|
||
rt.pivot = pivot;
|
||
rt.sizeDelta = size;
|
||
rt.anchoredPosition = position;
|
||
}
|
||
|
||
// ---------------- BUILD SETTINGS ----------------
|
||
|
||
static void UpdateBuildSettings()
|
||
{
|
||
var scenes = new List<EditorBuildSettingsScene>
|
||
{
|
||
new EditorBuildSettingsScene(CityScenePath, true),
|
||
new EditorBuildSettingsScene(WorldMapScenePath, true)
|
||
};
|
||
|
||
// Остальные уже добавленные сцены сохраняем (кроме удалённых файлов, например старой Battle)
|
||
foreach (var existing in EditorBuildSettings.scenes)
|
||
{
|
||
if (existing.path == CityScenePath || existing.path == WorldMapScenePath) continue;
|
||
if (!File.Exists(existing.path)) continue;
|
||
scenes.Add(existing);
|
||
}
|
||
|
||
EditorBuildSettings.scenes = scenes.ToArray();
|
||
}
|
||
|
||
// ---------------- КНОПКА В ГОРОДЕ ----------------
|
||
|
||
static void HookUpCityBattleButton()
|
||
{
|
||
var city = EditorSceneManager.OpenScene(CityScenePath, OpenSceneMode.Single);
|
||
|
||
GameObject navBattle = GameObject.Find("Nav_Battle");
|
||
if (navBattle == null)
|
||
{
|
||
Debug.LogWarning("[КАРТА] В сцене MVP1 не найден объект Nav_Battle -- повесь OpenBattleButton на кнопку БОЙ вручную.");
|
||
return;
|
||
}
|
||
|
||
if (navBattle.GetComponent<OpenBattleButton>() == null)
|
||
{
|
||
Undo.AddComponent<OpenBattleButton>(navBattle); // RequireComponent сам добавит Button, если его нет
|
||
EditorSceneManager.MarkSceneDirty(city);
|
||
EditorSceneManager.SaveScene(city);
|
||
}
|
||
}
|
||
|
||
// ---------------- HELPERS ----------------
|
||
|
||
static RectTransform CreatePanel(RectTransform parent, string name, Color color)
|
||
{
|
||
var go = new GameObject(name, typeof(RectTransform), typeof(Image));
|
||
go.transform.SetParent(parent, false);
|
||
go.GetComponent<Image>().color = color;
|
||
return go.GetComponent<RectTransform>();
|
||
}
|
||
|
||
static RectTransform CreateCenterPanel(RectTransform parent, string name, Vector2 size)
|
||
{
|
||
RectTransform rt = CreatePanel(parent, name, new Color(0.05f, 0.05f, 0.07f, 0.94f));
|
||
rt.anchorMin = rt.anchorMax = new Vector2(0.5f, 0.5f);
|
||
rt.sizeDelta = size;
|
||
return rt;
|
||
}
|
||
|
||
static RectTransform CreateTextRect(RectTransform parent, string name, string content, int fontSize,
|
||
TextAlignmentOptions alignment = TextAlignmentOptions.Center)
|
||
{
|
||
var go = new GameObject(name, typeof(RectTransform), typeof(TextMeshProUGUI));
|
||
go.transform.SetParent(parent, false);
|
||
var tmp = go.GetComponent<TextMeshProUGUI>();
|
||
tmp.text = content;
|
||
tmp.fontSize = fontSize;
|
||
tmp.color = Color.white;
|
||
tmp.alignment = alignment;
|
||
tmp.raycastTarget = false;
|
||
RectTransform rt = go.GetComponent<RectTransform>();
|
||
rt.anchorMin = Vector2.zero;
|
||
rt.anchorMax = Vector2.one;
|
||
rt.offsetMin = Vector2.zero;
|
||
rt.offsetMax = Vector2.zero;
|
||
return rt;
|
||
}
|
||
|
||
static TMP_Text CreateText(RectTransform parent, string name, string content, int fontSize)
|
||
{
|
||
return CreateTextRect(parent, name, content, fontSize).GetComponent<TMP_Text>();
|
||
}
|
||
|
||
static RectTransform CreateButton(RectTransform parent, string name, string label, Color color, out TMP_Text labelText)
|
||
{
|
||
var go = new GameObject(name, typeof(RectTransform), typeof(Image), typeof(Button));
|
||
go.transform.SetParent(parent, false);
|
||
go.GetComponent<Image>().color = color;
|
||
RectTransform rt = go.GetComponent<RectTransform>();
|
||
labelText = CreateText(rt, "Label", label, 36);
|
||
return rt;
|
||
}
|
||
|
||
static void StyleSlider(GameObject sliderGO)
|
||
{
|
||
Image[] images = sliderGO.GetComponentsInChildren<Image>();
|
||
foreach (var img in images)
|
||
{
|
||
if (img.name == "Background") img.color = new Color(0.25f, 0.25f, 0.28f);
|
||
else if (img.name == "Fill") img.color = new Color(0.3f, 0.55f, 1f);
|
||
else if (img.name == "Handle") img.color = Color.white;
|
||
}
|
||
// Ручка крупнее -- удобнее тянуть пальцем
|
||
Transform handle = sliderGO.transform.Find("Handle Slide Area/Handle");
|
||
if (handle != null) ((RectTransform)handle).sizeDelta = new Vector2(50, 0);
|
||
}
|
||
|
||
static void AnchorStretch(RectTransform rt, bool top, float height)
|
||
{
|
||
float y = top ? 1 : 0;
|
||
rt.anchorMin = new Vector2(0, y);
|
||
rt.anchorMax = new Vector2(1, y);
|
||
rt.pivot = new Vector2(0.5f, y);
|
||
rt.sizeDelta = new Vector2(0, height);
|
||
rt.anchoredPosition = Vector2.zero;
|
||
}
|
||
|
||
static void AnchorSide(RectTransform rt, bool left, Vector2 size, float offset)
|
||
{
|
||
float x = left ? 0 : 1;
|
||
rt.anchorMin = new Vector2(x, 0.5f);
|
||
rt.anchorMax = new Vector2(x, 0.5f);
|
||
rt.pivot = new Vector2(x, 0.5f);
|
||
rt.sizeDelta = size;
|
||
rt.anchoredPosition = new Vector2(left ? offset : -offset, 0);
|
||
}
|
||
|
||
// Растянуть по ширине родителя, прижать к верху с отступом
|
||
static void AnchorTop(RectTransform rt, float height, float topOffset)
|
||
{
|
||
rt.anchorMin = new Vector2(0, 1);
|
||
rt.anchorMax = new Vector2(1, 1);
|
||
rt.pivot = new Vector2(0.5f, 1);
|
||
rt.sizeDelta = new Vector2(0, height);
|
||
rt.anchoredPosition = new Vector2(0, -topOffset);
|
||
}
|
||
|
||
static void AnchorBottom(RectTransform rt, Vector2 size, Vector2 offset)
|
||
{
|
||
rt.anchorMin = rt.anchorMax = new Vector2(0.5f, 0);
|
||
rt.pivot = new Vector2(0.5f, 0);
|
||
rt.sizeDelta = size;
|
||
rt.anchoredPosition = offset;
|
||
}
|
||
}
|