Files
MVP1/Assets/Editor/WorldMapSceneBuilder.cs
AlexanderandClaude Opus 5.5 15b06b8f42 Модули Чата и Круга, разворот чата на весь экран, кнопка «Почта» в Safe Area
Модуль Чата (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>
2026-10-19 19:07:45 +02:00

527 lines
26 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}