2026-10-12 23:54:03 +03:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using UnityEngine.UI;
|
|
|
|
|
|
using TMPro;
|
|
|
|
|
|
|
2026-10-28 22:11:36 +03:00
|
|
|
|
// СВОБОДНАЯ СТРОЙКА НА СЕТКЕ (игрок сам выбирает место):
|
|
|
|
|
|
// 1. Кнопка "СТРОЙКА" включает режим: на земле появляется сетка (красные клетки заняты), сверху подсказка,
|
|
|
|
|
|
// и сразу открывается список зданий.
|
|
|
|
|
|
// 2. Выбрали здание -> появляется призрак: полупрозрачное здание и пятно его клеток.
|
|
|
|
|
|
// Зелёное пятно -- можно ставить, красное -- занято или за границей сетки.
|
|
|
|
|
|
// 3. Призрак двигают пальцем (тащить за само пятно) или тапом по земле -- он прыгает туда.
|
|
|
|
|
|
// Камера в это время не ездит. Размер у зданий разный (BuildingData.footprint).
|
|
|
|
|
|
// 4. Внизу панель: "Поставить" (если место свободно и хватает ресурсов), "Другое здание", "Отмена".
|
|
|
|
|
|
// 5. После постройки режим остаётся включённым -- можно строить дальше. "Выйти" -- сетка прячется.
|
2026-10-12 23:54:03 +03:00
|
|
|
|
//
|
|
|
|
|
|
// Вешается билдером (Tools -> Build HUD Panels) на HUD_Canvas, кнопка Nav_Build вызывает ToggleMode().
|
2026-10-28 22:11:36 +03:00
|
|
|
|
[DefaultExecutionOrder(-50)] // раньше камеры: захват пальца призраком должен быть виден ей в том же кадре
|
2026-10-12 23:54:03 +03:00
|
|
|
|
public class BuildModeController : MonoBehaviour
|
|
|
|
|
|
{
|
|
|
|
|
|
public static BuildModeController Instance { get; private set; }
|
|
|
|
|
|
|
|
|
|
|
|
[Tooltip("Кнопка HUD, которая включает/выключает режим (надпись меняется сама)")]
|
|
|
|
|
|
public Button modeButton;
|
|
|
|
|
|
[Tooltip("Старое меню стройки -- прячем, оно больше не используется")]
|
|
|
|
|
|
public GameObject legacyBuildMenu;
|
|
|
|
|
|
|
|
|
|
|
|
public bool IsActive { get; private set; }
|
|
|
|
|
|
|
|
|
|
|
|
GameObject banner;
|
|
|
|
|
|
GameObject chooser;
|
2026-10-28 22:11:36 +03:00
|
|
|
|
GameObject placePanel;
|
|
|
|
|
|
TextMeshProUGUI placeInfo;
|
|
|
|
|
|
Button placeButton;
|
|
|
|
|
|
|
|
|
|
|
|
BuildGhost ghost;
|
|
|
|
|
|
bool dragging;
|
|
|
|
|
|
Vector2 grabOffset;
|
2026-10-12 23:54:03 +03:00
|
|
|
|
Vector2 downPos;
|
|
|
|
|
|
bool downOverUI;
|
2026-10-28 22:11:36 +03:00
|
|
|
|
bool downOnGhost;
|
|
|
|
|
|
float nextPanelRefresh;
|
2026-10-12 23:54:03 +03:00
|
|
|
|
|
2026-10-28 22:11:36 +03:00
|
|
|
|
const float TapThreshold = 25f;
|
2026-10-12 23:54:03 +03:00
|
|
|
|
|
|
|
|
|
|
void Awake() { Instance = this; }
|
2026-10-28 22:11:36 +03:00
|
|
|
|
|
|
|
|
|
|
void OnDestroy()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Instance == this) Instance = null;
|
|
|
|
|
|
UIBlocker.WorldInputCaptured = false;
|
|
|
|
|
|
}
|
2026-10-12 23:54:03 +03:00
|
|
|
|
|
|
|
|
|
|
void Start()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (legacyBuildMenu != null) legacyBuildMenu.SetActive(false);
|
|
|
|
|
|
SetMode(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void ToggleMode() { SetMode(!IsActive); }
|
|
|
|
|
|
|
|
|
|
|
|
public void SetMode(bool active)
|
|
|
|
|
|
{
|
|
|
|
|
|
IsActive = active;
|
|
|
|
|
|
CloseChooser();
|
2026-10-28 22:11:36 +03:00
|
|
|
|
CancelGhost();
|
2026-10-12 23:54:03 +03:00
|
|
|
|
UpdateButtonLabel();
|
2026-10-28 22:11:36 +03:00
|
|
|
|
BuildGrid.Instance.ShowOverlay(active);
|
2026-10-12 23:54:03 +03:00
|
|
|
|
|
|
|
|
|
|
if (banner != null) { Destroy(banner); banner = null; }
|
|
|
|
|
|
if (active)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (UIPanelManager.Instance != null) UIPanelManager.Instance.CloseAll();
|
|
|
|
|
|
banner = CreateBanner();
|
2026-10-28 22:11:36 +03:00
|
|
|
|
OpenChooser();
|
2026-10-12 23:54:03 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Update()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!IsActive) return;
|
|
|
|
|
|
// открыли любую панель HUD -- выходим из режима стройки
|
|
|
|
|
|
if (UIPanelManager.Instance != null && UIPanelManager.Instance.CurrentId != "") { SetMode(false); return; }
|
2026-10-28 22:11:36 +03:00
|
|
|
|
if (chooser != null || ghost == null) { StopDrag(); return; }
|
|
|
|
|
|
|
|
|
|
|
|
HandlePointer();
|
|
|
|
|
|
|
|
|
|
|
|
if (Time.unscaledTime >= nextPanelRefresh)
|
|
|
|
|
|
{
|
|
|
|
|
|
nextPanelRefresh = Time.unscaledTime + 0.3f;
|
|
|
|
|
|
ghost.Recheck(); // клетки могли заняться (загрузка сохранения) или ресурсы -- прийти
|
|
|
|
|
|
RefreshPlacePanel();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------- палец: тащим призрак или тапаем по земле ----------------
|
|
|
|
|
|
|
|
|
|
|
|
void HandlePointer()
|
|
|
|
|
|
{
|
|
|
|
|
|
Vector2 world = PointerWorld();
|
2026-10-12 23:54:03 +03:00
|
|
|
|
|
|
|
|
|
|
if (Input.GetMouseButtonDown(0))
|
|
|
|
|
|
{
|
|
|
|
|
|
downPos = Input.mousePosition;
|
|
|
|
|
|
downOverUI = UIBlocker.IsPointerOverUI();
|
2026-10-28 22:11:36 +03:00
|
|
|
|
downOnGhost = !downOverUI && ghost.Contains(world);
|
|
|
|
|
|
if (downOnGhost)
|
|
|
|
|
|
{
|
|
|
|
|
|
dragging = true;
|
|
|
|
|
|
grabOffset = (Vector2)ghost.transform.position - world;
|
|
|
|
|
|
UIBlocker.WorldInputCaptured = true; // камера не поедет
|
|
|
|
|
|
}
|
2026-10-12 23:54:03 +03:00
|
|
|
|
}
|
2026-10-28 22:11:36 +03:00
|
|
|
|
|
|
|
|
|
|
if (dragging && Input.GetMouseButton(0))
|
|
|
|
|
|
{
|
|
|
|
|
|
var before = ghost.Cell;
|
|
|
|
|
|
ghost.MoveNear(world + grabOffset);
|
|
|
|
|
|
if (ghost.Cell != before) RefreshPlacePanel();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-12 23:54:03 +03:00
|
|
|
|
if (Input.GetMouseButtonUp(0))
|
|
|
|
|
|
{
|
2026-10-28 22:11:36 +03:00
|
|
|
|
bool wasDragging = dragging;
|
|
|
|
|
|
StopDrag();
|
|
|
|
|
|
if (wasDragging || downOverUI || UIBlocker.IsPointerOverUI()) return;
|
|
|
|
|
|
if (((Vector2)Input.mousePosition - downPos).magnitude > TapThreshold) return; // тащили камеру
|
|
|
|
|
|
ghost.MoveNear(world); // тап по земле -- призрак прыгает сюда
|
|
|
|
|
|
RefreshPlacePanel();
|
2026-10-12 23:54:03 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-28 22:11:36 +03:00
|
|
|
|
void StopDrag()
|
2026-10-12 23:54:03 +03:00
|
|
|
|
{
|
2026-10-28 22:11:36 +03:00
|
|
|
|
dragging = false;
|
|
|
|
|
|
UIBlocker.WorldInputCaptured = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static Vector2 PointerWorld()
|
|
|
|
|
|
{
|
|
|
|
|
|
var cam = Camera.main;
|
|
|
|
|
|
if (cam == null) return Vector2.zero;
|
|
|
|
|
|
Vector3 w = cam.ScreenToWorldPoint(Input.mousePosition);
|
|
|
|
|
|
return new Vector2(w.x, w.y);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------- призрак ----------------
|
|
|
|
|
|
|
|
|
|
|
|
void StartGhost(BuildingData data)
|
|
|
|
|
|
{
|
|
|
|
|
|
CancelGhost();
|
|
|
|
|
|
CloseChooser();
|
|
|
|
|
|
ghost = BuildGhost.Create(data);
|
|
|
|
|
|
|
|
|
|
|
|
// ставим в центр экрана, а если там занято -- на ближайшее свободное место
|
|
|
|
|
|
var cam = Camera.main;
|
|
|
|
|
|
Vector2 center = cam != null ? (Vector2)cam.transform.position : Vector2.zero;
|
|
|
|
|
|
if (BuildGrid.Instance.FindFreeNear(center, ghost.Footprint.size, out var cell)) ghost.MoveToCell(cell);
|
|
|
|
|
|
else
|
2026-10-12 23:54:03 +03:00
|
|
|
|
{
|
2026-10-28 22:11:36 +03:00
|
|
|
|
ghost.MoveNear(center);
|
|
|
|
|
|
UIKit.Toast("Свободного места под это здание нет");
|
2026-10-12 23:54:03 +03:00
|
|
|
|
}
|
2026-10-28 22:11:36 +03:00
|
|
|
|
CreatePlacePanel();
|
2026-10-12 23:54:03 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-28 22:11:36 +03:00
|
|
|
|
void CancelGhost()
|
2026-10-12 23:54:03 +03:00
|
|
|
|
{
|
2026-10-28 22:11:36 +03:00
|
|
|
|
StopDrag();
|
|
|
|
|
|
if (ghost != null) Destroy(ghost.gameObject);
|
|
|
|
|
|
ghost = null;
|
|
|
|
|
|
if (placePanel != null) Destroy(placePanel);
|
|
|
|
|
|
placePanel = null;
|
2026-10-12 23:54:03 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-28 22:11:36 +03:00
|
|
|
|
void Place()
|
2026-10-12 23:54:03 +03:00
|
|
|
|
{
|
2026-10-28 22:11:36 +03:00
|
|
|
|
if (ghost == null || BuildManager.Instance == null) return;
|
|
|
|
|
|
var d = ghost.Data;
|
|
|
|
|
|
ghost.Recheck();
|
|
|
|
|
|
if (!ghost.CellsFree) { UIKit.Toast("Здесь строить нельзя: клетки заняты"); return; }
|
|
|
|
|
|
if (!d.CanAfford()) { UIKit.Toast(GameProgress.Instance.MissingText(ToRewards(d.cost))); return; }
|
|
|
|
|
|
|
|
|
|
|
|
if (BuildManager.Instance.BuildAt(d, ghost.Cell) != null)
|
2026-10-12 23:54:03 +03:00
|
|
|
|
{
|
2026-10-28 22:11:36 +03:00
|
|
|
|
UIKit.Toast("Построено: " + d.buildingName);
|
|
|
|
|
|
CancelGhost(); // режим остаётся: "Здания" в подсказке сверху -- строить дальше
|
2026-10-12 23:54:03 +03:00
|
|
|
|
}
|
2026-10-28 22:11:36 +03:00
|
|
|
|
else UIKit.Toast("Не получилось построить");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------- нижняя панель: Поставить / Другое здание / Отмена ----------------
|
|
|
|
|
|
|
|
|
|
|
|
void CreatePlacePanel()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (placePanel != null) Destroy(placePanel);
|
|
|
|
|
|
var bar = UIKit.Row(UIKit.Overlay, -1, 14, new Color(0.08f, 0.1f, 0.14f, 0.94f), 14);
|
|
|
|
|
|
bar.name = "BuildPlacePanel";
|
|
|
|
|
|
bar.anchorMin = new Vector2(0.04f, 0); bar.anchorMax = new Vector2(0.96f, 0);
|
|
|
|
|
|
bar.pivot = new Vector2(0.5f, 0);
|
|
|
|
|
|
bar.sizeDelta = new Vector2(0, 130); bar.anchoredPosition = new Vector2(0, 40);
|
|
|
|
|
|
placePanel = bar.gameObject;
|
|
|
|
|
|
|
|
|
|
|
|
placeInfo = UIKit.Text(bar, "", 26, Color.white, TextAlignmentOptions.Left);
|
|
|
|
|
|
UIKit.Size(placeInfo, -1, -1, 1);
|
|
|
|
|
|
placeButton = UIKit.Button(bar, "Поставить", UIKit.Theme.Green, Place, 230, 90, 28);
|
|
|
|
|
|
UIKit.Button(bar, "Другое", UIKit.Theme.Blue, OpenChooser, 180, 90, 26);
|
|
|
|
|
|
UIKit.Button(bar, "Отмена", UIKit.Theme.Gray, CancelGhost, 180, 90, 26);
|
|
|
|
|
|
RefreshPlacePanel();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RefreshPlacePanel()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ghost == null || placeInfo == null) return;
|
|
|
|
|
|
var d = ghost.Data;
|
|
|
|
|
|
bool afford = d.CanAfford();
|
|
|
|
|
|
string where = ghost.CellsFree ? "<color=#9be59b>место свободно</color>" : "<color=#ff7a7a>клетки заняты</color>";
|
|
|
|
|
|
placeInfo.text = "<b>" + d.buildingName + "</b> " + ghost.Footprint.size.x + "×" + ghost.Footprint.size.y +
|
|
|
|
|
|
" кл. • " + where + "\n" + CostText(d);
|
|
|
|
|
|
bool ok = ghost.CellsFree && afford;
|
|
|
|
|
|
UIKit.SetButton(placeButton, ok ? "Поставить" : (ghost.CellsFree ? "Мало ресурсов" : "Занято"), ok);
|
2026-10-12 23:54:03 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------- окно выбора здания ----------------
|
|
|
|
|
|
|
2026-10-28 22:11:36 +03:00
|
|
|
|
void OpenChooser()
|
2026-10-12 23:54:03 +03:00
|
|
|
|
{
|
|
|
|
|
|
CloseChooser();
|
2026-10-28 22:11:36 +03:00
|
|
|
|
StopDrag();
|
2026-10-12 23:54:03 +03:00
|
|
|
|
var bm = BuildManager.Instance;
|
2026-10-28 22:11:36 +03:00
|
|
|
|
if (bm == null) return;
|
2026-10-12 23:54:03 +03:00
|
|
|
|
GameObject root;
|
|
|
|
|
|
var card = UIKit.Modal("Что построить?", out root);
|
|
|
|
|
|
chooser = root;
|
|
|
|
|
|
|
|
|
|
|
|
// окно фиксированного размера с прокруткой, чтобы влезало на любом экране
|
|
|
|
|
|
var fit = card.GetComponent<ContentSizeFitter>();
|
|
|
|
|
|
if (fit != null) DestroyImmediate(fit);
|
|
|
|
|
|
card.anchorMin = new Vector2(0.08f, 0.08f); card.anchorMax = new Vector2(0.92f, 0.92f);
|
|
|
|
|
|
card.offsetMin = Vector2.zero; card.offsetMax = Vector2.zero;
|
|
|
|
|
|
|
|
|
|
|
|
int th = TownHall.Instance != null ? TownHall.Instance.currentLevel : 1;
|
2026-10-28 22:11:36 +03:00
|
|
|
|
UIKit.Text(card, "Ратуша " + th + " ур. • выберите здание, потом место на сетке", 24, UIKit.Theme.TextDim, TextAlignmentOptions.Center);
|
2026-10-12 23:54:03 +03:00
|
|
|
|
|
|
|
|
|
|
var holder = UIKit.Make("ListHolder", card);
|
|
|
|
|
|
UIKit.Size(holder, -1, -1).flexibleHeight = 1;
|
|
|
|
|
|
ScrollRect scroll;
|
|
|
|
|
|
var list = UIKit.ScrollList(holder, out scroll, 10);
|
|
|
|
|
|
|
|
|
|
|
|
// доступные сверху, закрытые по уровню Ратуши -- ниже
|
|
|
|
|
|
var all = new List<BuildingData>(bm.allBuildings);
|
2026-10-28 22:11:36 +03:00
|
|
|
|
all.RemoveAll(b => b == null || b.prefab == null);
|
2026-10-12 23:54:03 +03:00
|
|
|
|
all.Sort((a, b) => a.requiredTownHallLevel.CompareTo(b.requiredTownHallLevel));
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var data in all)
|
|
|
|
|
|
{
|
|
|
|
|
|
var d = data;
|
|
|
|
|
|
bool locked = d.requiredTownHallLevel > th;
|
|
|
|
|
|
bool afford = !locked && d.CanAfford();
|
2026-10-28 22:11:36 +03:00
|
|
|
|
var size = BuildFootprint.Of(d).size;
|
2026-10-12 23:54:03 +03:00
|
|
|
|
|
|
|
|
|
|
var row = UIKit.Row(list, 120, 14, locked ? UIKit.Theme.CardDark : UIKit.Theme.Card, 10);
|
|
|
|
|
|
var icon = UIKit.Box(row, d.icon != null ? Color.white : UIKit.Theme.Gray);
|
|
|
|
|
|
if (d.icon != null) { icon.sprite = d.icon; icon.preserveAspect = true; }
|
|
|
|
|
|
UIKit.Size(icon, 100, 100);
|
|
|
|
|
|
|
|
|
|
|
|
var info = UIKit.Card(row, new Color(0, 0, 0, 0), 0, 4);
|
|
|
|
|
|
UIKit.Size(info, -1, -1, 1);
|
2026-10-28 22:11:36 +03:00
|
|
|
|
UIKit.Text(info, d.buildingName + " <size=22><color=#aab>" + size.x + "×" + size.y + " кл.</color></size>", 30,
|
|
|
|
|
|
locked ? UIKit.Theme.TextDim : UIKit.Theme.Text).fontStyle = FontStyles.Bold;
|
2026-10-12 23:54:03 +03:00
|
|
|
|
UIKit.Text(info, locked ? "Откроется на Ратуше " + d.requiredTownHallLevel + " уровня" : CostText(d), 24,
|
|
|
|
|
|
locked ? UIKit.Theme.Gold : UIKit.Theme.TextDim);
|
|
|
|
|
|
|
2026-10-28 22:11:36 +03:00
|
|
|
|
// выбрать можно и без ресурсов: место присмотреть, а "Поставить" загорится, когда хватит
|
|
|
|
|
|
string label = locked ? "Закрыто" : "Выбрать";
|
|
|
|
|
|
var b = UIKit.Button(row, label, afford ? UIKit.Theme.Green : UIKit.Theme.Gray, () => StartGhost(d), 230, 90, 26);
|
2026-10-12 23:54:03 +03:00
|
|
|
|
b.interactable = !locked;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-28 22:11:36 +03:00
|
|
|
|
UIKit.Button(card, "Закрыть", UIKit.Theme.Gray, CloseChooser, -1, 90, 30);
|
2026-10-12 23:54:03 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CloseChooser()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (chooser != null) Destroy(chooser);
|
|
|
|
|
|
chooser = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static RewardItem[] ToRewards(ResourceCost[] cost)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (cost == null) return new RewardItem[0];
|
|
|
|
|
|
var r = new RewardItem[cost.Length];
|
|
|
|
|
|
for (int i = 0; i < cost.Length; i++) r[i] = new RewardItem(cost[i].type, Mathf.CeilToInt(cost[i].amount));
|
|
|
|
|
|
return r;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Цена по-русски, красным -- чего не хватает
|
|
|
|
|
|
static string CostText(BuildingData d)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (d.cost == null || d.cost.Length == 0) return "Бесплатно";
|
|
|
|
|
|
var parts = new List<string>();
|
|
|
|
|
|
foreach (var c in d.cost)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool ok = ResourceManager.Instance != null && ResourceManager.Instance.HasEnough(c.type, c.amount);
|
|
|
|
|
|
parts.Add("<color=" + (ok ? "#9be59b" : "#ff7a7a") + ">" + GameData.Short(c.amount) + "</color> " + GameData.ResName(c.type));
|
|
|
|
|
|
}
|
|
|
|
|
|
return string.Join(", ", parts.ToArray());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------- подсказка сверху и кнопка ----------------
|
|
|
|
|
|
|
|
|
|
|
|
GameObject CreateBanner()
|
|
|
|
|
|
{
|
|
|
|
|
|
// полоска сверху экрана (не модальная: остальной экран остаётся кликабельным)
|
|
|
|
|
|
var bar = UIKit.Row(UIKit.Overlay, -1, 16, new Color(0.1f, 0.25f, 0.12f, 0.92f), 14);
|
|
|
|
|
|
bar.name = "BuildModeBanner";
|
2026-10-28 22:11:36 +03:00
|
|
|
|
bar.anchorMin = new Vector2(0.1f, 1); bar.anchorMax = new Vector2(0.9f, 1);
|
2026-10-12 23:54:03 +03:00
|
|
|
|
bar.pivot = new Vector2(0.5f, 1);
|
|
|
|
|
|
bar.sizeDelta = new Vector2(0, 110); bar.anchoredPosition = new Vector2(0, -150);
|
2026-10-28 22:11:36 +03:00
|
|
|
|
var t = UIKit.Text(bar, "РЕЖИМ СТРОЙКИ: двигайте здание по сетке, красное -- занято", 26, Color.white, TextAlignmentOptions.Left);
|
2026-10-12 23:54:03 +03:00
|
|
|
|
UIKit.Size(t, -1, -1, 1);
|
2026-10-28 22:11:36 +03:00
|
|
|
|
UIKit.Button(bar, "Здания", UIKit.Theme.Green, OpenChooser, 180, 80, 28);
|
|
|
|
|
|
UIKit.Button(bar, "Выйти", UIKit.Theme.Red, () => SetMode(false), 160, 80, 28);
|
2026-10-12 23:54:03 +03:00
|
|
|
|
return bar.gameObject;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void UpdateButtonLabel()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (modeButton == null) return;
|
|
|
|
|
|
var t = modeButton.GetComponentInChildren<TextMeshProUGUI>();
|
|
|
|
|
|
if (t != null) t.text = IsActive ? "ГОТОВО" : "СТРОЙКА";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|