using System.Collections.Generic; using TMPro; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; using BaseGames.UI; using BaseGames.UI.Menus; using BaseGames.World.Map; using BaseGames.Localization; namespace BaseGames.Editor.UI { /// /// 地图 UI 脚手架(对照 )。 /// 在当前活动场景生成完整的地图 UI 层级与预制,并按规范绑定引用: /// /// Cell / Pin / ExitConnector 预制 + MapPinConfig 占位资产 /// HUD 下小地图(MinimapHUD + MinimapInputHandler) /// Map Canvas 下全屏地图(MapPanel + MapInputHandler,PanelStack 管理) /// 传送确认框(ConfirmDialogController)+ MapTeleportConfirmController(接 MapPanel) /// 登记 UIManager._panels[Map],绑定 EVT_MapOpen /// /// 执行路径:BaseGames ▸ Scene ▸ Setup ▸ Scaffold Map UI /// 占位为纯色块/空 Sprite,美术后续替换;运行依赖 [GameManagers] 下的 /// MapManager / MapPlayerTracker / MapPinManager / TeleportService 已存在(另由 Persistent 脚手架搭建)。 /// public static class MapUIScaffoldWizard { private const string UiPrefabDir = "Assets/_Game/Prefabs/UI"; private const string MapDataDir = "Assets/_Game/Data/Map"; [MenuItem("BaseGames/Scene/Setup/Scaffold Map UI", priority = 204)] public static void ScaffoldMapUI() { var report = new List(); Undo.SetCurrentGroupName("Scaffold Map UI"); int undoGroup = Undo.GetCurrentGroup(); // ── 共享资产:Cell / Pin / Exit 预制 + PinConfig ────────────────── MapPinConfigSO pinConfig = EnsurePinConfig(report); GameObject cellPrefab = EnsureCellPrefab(report); GameObject pinPrefab = EnsureSimpleImagePrefab("UI_Map_Pin", new Color32(0xF0, 0xC0, 0x40, 0xFF), new Vector2(14, 14), report); GameObject exitPrefab = EnsureSimpleImagePrefab("UI_Map_ExitConnector", new Color32(0xC0, 0xC0, 0xC0, 0xCC), new Vector2(8, 16), report); Sprite playerDotSprite = null; // 占位用纯色,留空即可 // ── 小地图(HUD Canvas 下)──────────────────────────────────────── GameObject hudCanvas = FindHudCanvas(); if (hudCanvas == null) report.Add("未找到 HUD Canvas,请先执行 BaseGames/Scene/Setup/Scaffold HUD Canvas;本次跳过小地图/区域名搭建。"); else { // 按需求不搭建右上角小地图,只保留全屏大地图;保留进入区域时的区域名横幅。 BuildRegionBanner(hudCanvas, report); report.Add("已跳过小地图(MinimapHUD)搭建:按需求只保留全屏大地图。"); } // ── 全屏地图(独立 Map Canvas,PanelStack 管理)─────────────────── GameObject mapPanelRoot = BuildFullMap(cellPrefab, exitPrefab, pinPrefab, pinConfig, report); // ── 传送确认框 + 控制器 ─────────────────────────────────────────── BuildTeleportConfirm(mapPanelRoot, report); // ── 登记 UIManager._panels[Map] + 绑定 EVT_MapOpen ──────────────── RegisterMapPanelWithUIManager(mapPanelRoot, report); Undo.CollapseUndoOperations(undoGroup); AssetDatabase.SaveAssets(); MarkDirtyAndLog("Map UI 脚手架", mapPanelRoot != null ? mapPanelRoot : hudCanvas, report); } // ───────────────────────────────────────────────────────────────────── // 预制 / 资产 // ───────────────────────────────────────────────────────────────────── /// 创建(或复用)MapRoomCellUI 预制:_bg 为 Raycast Target(可点击传送),其余子图不挡射线。 private static GameObject EnsureCellPrefab(List report) { string path = $"{UiPrefabDir}/UI_Map_RoomCell.prefab"; var existing = AssetDatabase.LoadAssetAtPath(path); if (existing != null) return existing; EnsureFolder(UiPrefabDir); var root = new GameObject("UI_Map_RoomCell", typeof(RectTransform)); ((RectTransform)root.transform).sizeDelta = new Vector2(32, 32); // 背景(可见 + 可点击) var bg = MakeImage(root.transform, "BG", new Color(1f, 1f, 1f, 1f), new Vector2(32, 32), raycast: true); // 轮廓(RawImage,默认禁用) var outlineGo = MakeChild(root.transform, "Outline", new Vector2(32, 32)); var outline = outlineGo.AddComponent(); outline.raycastTarget = false; outline.enabled = false; // 图标 / 高亮 / 雾 / 传送标记(均不挡射线,运行时按需启用) var icon = MakeImage(root.transform, "Icon", new Color(1, 1, 1, 1), new Vector2(20, 20), raycast: false); icon.enabled = false; var highlight = MakeImage(root.transform, "Highlight", new Color(1f, 0.9f, 0.2f, 1f),new Vector2(34, 34), raycast: false); highlight.enabled = false; var fog = MakeImage(root.transform, "Fog", new Color(0, 0, 0, 0.85f), new Vector2(32, 32), raycast: false); fog.enabled = false; var teleport = MakeImage(root.transform, "TeleportMarker", new Color(0.3f, 0.8f, 1f, 1f), new Vector2(12, 12), raycast: false); teleport.enabled = false; var cell = root.AddComponent(); AssignRef(cell, "_bg", bg); AssignRef(cell, "_icon", icon); AssignRef(cell, "_outlineImage", outline); AssignRef(cell, "_highlight", highlight); AssignRef(cell, "_fogOverlay", fog); AssignRef(cell, "_teleportMarker",teleport); var prefab = PrefabUtility.SaveAsPrefabAsset(root, path); Object.DestroyImmediate(root); report.Add($"已创建 Cell 预制:{path}(占位纯色,美术可替换)。"); return prefab; } private static GameObject EnsureSimpleImagePrefab(string name, Color color, Vector2 size, List report) { string path = $"{UiPrefabDir}/{name}.prefab"; var existing = AssetDatabase.LoadAssetAtPath(path); if (existing != null) return existing; EnsureFolder(UiPrefabDir); var go = new GameObject(name, typeof(RectTransform)); ((RectTransform)go.transform).sizeDelta = size; var img = go.AddComponent(); img.color = color; img.raycastTarget = false; var prefab = PrefabUtility.SaveAsPrefabAsset(go, path); Object.DestroyImmediate(go); report.Add($"已创建预制:{path}(占位纯色,美术可替换)。"); return prefab; } private static MapPinConfigSO EnsurePinConfig(List report) { string path = $"{MapDataDir}/MapPinConfig.asset"; var existing = AssetDatabase.LoadAssetAtPath(path); if (existing != null) return existing; EnsureFolder(MapDataDir); var so = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(so, path); report.Add($"已创建占位 MapPinConfig:{path}(_entries 为空,请配置 PinType→Sprite 映射)。"); return so; } // ───────────────────────────────────────────────────────────────────── // 小地图(HUD) // ───────────────────────────────────────────────────────────────────── private static void BuildMinimap(GameObject hudCanvas, GameObject cellPrefab, GameObject pinPrefab, MapPinConfigSO pinConfig, List report) { Transform hudRoot = hudCanvas.transform.Find("HUDRoot") ?? hudCanvas.transform; var minimapGo = GetOrCreateChild(hudRoot, "Minimap").gameObject; var mmRect = minimapGo.GetComponent() ?? minimapGo.AddComponent(); AnchorTopRight(mmRect, new Vector2(180, 180), new Vector2(-16, -16)); // 边框底图(可见,便于定位;美术可替换/移除) var frame = minimapGo.GetComponent() ?? minimapGo.AddComponent(); frame.color = new Color(0f, 0f, 0f, 0.35f); frame.raycastTarget = false; // 带 RectMask2D 的内容容器(cell 在此平移) var viewportGo = GetOrCreateChild(minimapGo.transform, "Viewport").gameObject; var vpRect = viewportGo.GetComponent() ?? viewportGo.AddComponent(); StretchFill(vpRect, 6f); if (viewportGo.GetComponent() == null) viewportGo.AddComponent(); // 玩家圆点(在容器内) var playerDot = MakeImage(viewportGo.transform, "PlayerDot", new Color(1f, 0.25f, 0.25f, 1f), new Vector2(8, 8), raycast: false); var hud = GetOrAddComponent(minimapGo); var input = GetOrAddComponent(minimapGo); AssignRef(hud, "_cellPrefab", cellPrefab.GetComponent()); AssignRef(hud, "_cellContainer",vpRect); AssignRef(hud, "_playerDot", playerDot); AssignRef(hud, "_pinPrefab", pinPrefab.GetComponent()); AssignRef(hud, "_pinConfig", pinConfig); AssignMapIcons(hud, report); AssignAsset(input, "_inputReader", report, true, "InputReader"); report.Add("Minimap 已搭建于 HUDRoot(右上角占位框)。美术可调整位置/尺寸/边框。"); } /// 进入新区域时屏幕中央渐显区域名横幅(RegionNameDisplay,挂 HUDRoot)。 private static void BuildRegionBanner(GameObject hudCanvas, List report) { Transform hudRoot = hudCanvas.transform.Find("HUDRoot") ?? hudCanvas.transform; var bannerGo = GetOrCreateChild(hudRoot, "RegionNameBanner").gameObject; var br = bannerGo.GetComponent() ?? bannerGo.AddComponent(); br.anchorMin = br.anchorMax = new Vector2(0.5f, 0.72f); br.pivot = new Vector2(0.5f, 0.5f); br.sizeDelta = new Vector2(640f, 90f); br.anchoredPosition = Vector2.zero; if (bannerGo.GetComponent() == null) bannerGo.AddComponent(); var txt = MakeText(bannerGo.transform, "RegionText", "区域名"); txt.fontSize = 48; StretchFill((RectTransform)txt.transform, 0f); var rnd = GetOrAddComponent(bannerGo); AssignRef(rnd, "_regionText", txt); AssignAsset(rnd, "_onRegionChanged", report, false, "EVT_RegionChanged"); report.Add("RegionNameDisplay(进入区域时渐显区域名横幅)已搭建于 HUDRoot。"); } // ───────────────────────────────────────────────────────────────────── // 全屏地图(独立 Canvas) // ───────────────────────────────────────────────────────────────────── private static GameObject BuildFullMap(GameObject cellPrefab, GameObject exitPrefab, GameObject pinPrefab, MapPinConfigSO pinConfig, List report) { GameObject canvasGo = GetOrCreateMapCanvas("Map Canvas", 25); var panelGo = GetOrCreateChild(canvasGo.transform, "MapPanel").gameObject; StretchFill(panelGo.GetComponent() ?? panelGo.AddComponent(), 0f); // 半透明全屏底 var panelBg = panelGo.GetComponent() ?? panelGo.AddComponent(); panelBg.color = new Color(0.05f, 0.05f, 0.07f, 0.92f); // ScrollView → Viewport(RectMask2D) → RoomContainer(content) var scrollGo = GetOrCreateChild(panelGo.transform, "ScrollView").gameObject; StretchFill(scrollGo.GetComponent() ?? scrollGo.AddComponent(), 40f); var scrollRect = GetOrAddComponent(scrollGo); scrollRect.horizontal = true; scrollRect.vertical = true; scrollRect.movementType = ScrollRect.MovementType.Clamped; scrollRect.scrollSensitivity = 0f; // 缩放/平移由 MapInputHandler 处理 var viewportGo = GetOrCreateChild(scrollGo.transform, "Viewport").gameObject; var vpRect = viewportGo.GetComponent() ?? viewportGo.AddComponent(); StretchFill(vpRect, 0f); if (viewportGo.GetComponent() == null) viewportGo.AddComponent(); var vpImg = viewportGo.GetComponent() ?? viewportGo.AddComponent(); vpImg.color = new Color(0, 0, 0, 0.01f); // 近透明,作为 ScrollRect viewport 的图形 var contentGo = GetOrCreateChild(viewportGo.transform, "RoomContainer").gameObject; var contentRect = contentGo.GetComponent() ?? contentGo.AddComponent(); contentRect.anchorMin = contentRect.anchorMax = new Vector2(0.5f, 0.5f); contentRect.pivot = new Vector2(0.5f, 0.5f); contentRect.sizeDelta = new Vector2(4000, 4000); scrollRect.content = contentRect; scrollRect.viewport = vpRect; // 玩家图标(content 内) var playerIcon = MakeImage(contentGo.transform, "PlayerIcon", new Color(1f, 0.3f, 0.3f, 1f), new Vector2(16, 16), raycast: false); // Tooltip(默认隐藏) var tooltipGo = GetOrCreateChild(panelGo.transform, "Tooltip").gameObject; var ttRect = tooltipGo.GetComponent() ?? tooltipGo.AddComponent(); AnchorTopRight(ttRect, new Vector2(240, 60), new Vector2(-20, -20)); var ttImg = tooltipGo.GetComponent() ?? tooltipGo.AddComponent(); ttImg.color = new Color(0, 0, 0, 0.8f); ttImg.raycastTarget = false; var ttText = MakeText(tooltipGo.transform, "Text", "房间"); tooltipGo.SetActive(false); // 组件 var mapPanel = GetOrAddComponent(panelGo); var mapInput = GetOrAddComponent(panelGo); AssignRef(mapPanel, "_roomContainer", contentRect); AssignRef(mapPanel, "_cellPrefab", cellPrefab.GetComponent()); AssignRef(mapPanel, "_exitConnectorPrefab", exitPrefab.GetComponent()); AssignRef(mapPanel, "_scrollRect", scrollRect); AssignRef(mapPanel, "_playerIconImg", playerIcon); AssignRef(mapPanel, "_pinPrefab", pinPrefab.GetComponent()); AssignRef(mapPanel, "_pinConfig", pinConfig); AssignRef(mapPanel, "_tooltipPanel", tooltipGo); AssignRef(mapPanel, "_tooltipText", ttText); AssignMapIcons(mapPanel, report); AssignRefObj(mapPanel, "_iconPlayerPos", null); // 占位玩家图标用纯色,留空 AssignAsset(mapInput, "_inputReader", report, true, "InputReader"); AssignRef(mapInput, "_scrollRect", scrollRect); AssignRef(mapInput, "_zoomTarget", contentRect); // ── 探索进度(左上角,全局% + 当前区域%;格式串走本地化 Key)────── var progGo = GetOrCreateChild(panelGo.transform, "ProgressDisplay").gameObject; var progRect = progGo.GetComponent() ?? progGo.AddComponent(); progRect.anchorMin = progRect.anchorMax = new Vector2(0f, 1f); progRect.pivot = new Vector2(0f, 1f); progRect.anchoredPosition = new Vector2(28f, -24f); progRect.sizeDelta = new Vector2(380f, 96f); var globalTxt = MakeText(progGo.transform, "GlobalProgress", "0%"); var gRt = (RectTransform)globalTxt.transform; gRt.anchorMin = gRt.anchorMax = new Vector2(0f, 1f); gRt.pivot = new Vector2(0f, 1f); gRt.anchoredPosition = Vector2.zero; globalTxt.alignment = TextAlignmentOptions.TopLeft; var regionTxt = MakeText(progGo.transform, "RegionProgress", "0%"); var rRt = (RectTransform)regionTxt.transform; rRt.anchorMin = rRt.anchorMax = new Vector2(0f, 1f); rRt.pivot = new Vector2(0f, 1f); rRt.anchoredPosition = new Vector2(0f, -44f); regionTxt.alignment = TextAlignmentOptions.TopLeft; regionTxt.fontSize = 24; var prog = GetOrAddComponent(progGo); AssignRef(prog, "_globalProgressText", globalTxt); AssignRef(prog, "_regionProgressText", regionTxt); AssignString(prog, "_globalFormat", "MAP_PROGRESS_GLOBAL"); // 本地化 Key,MapProgressDisplay 运行时解析为格式串 AssignString(prog, "_regionFormat", "MAP_PROGRESS_REGION"); AssignAsset(prog, "_onRegionChanged", report, false, "EVT_RegionChanged"); // ── 关闭提示(底部居中):输入图标(随设备自适应) + 本地化标签 ────── var hintRow = GetOrCreateChild(panelGo.transform, "CloseHint").gameObject; var hintRowRt = hintRow.GetComponent() ?? hintRow.AddComponent(); hintRowRt.anchorMin = hintRowRt.anchorMax = new Vector2(0.5f, 0f); hintRowRt.pivot = new Vector2(0.5f, 0f); hintRowRt.anchoredPosition = new Vector2(0f, 24f); hintRowRt.sizeDelta = new Vector2(360f, 40f); var hintLayout = GetOrAddComponent(hintRow); hintLayout.childAlignment = TextAnchor.MiddleCenter; hintLayout.spacing = 8f; hintLayout.childForceExpandWidth = false; hintLayout.childForceExpandHeight = false; MakeInputIcon(hintRow.transform, "CloseIcon", "Cancel"); // 复用 InputIconImage:键鼠/手柄自动显示对应按键图标 var hintLabel = MakeLocalizedText(hintRow.transform, "CloseLabel", "MAP_CLOSE_HINT"); hintLabel.fontSize = 22; report.Add("关闭提示:InputIconImage(动作 'Cancel') + LocalizedText('MAP_CLOSE_HINT')。" + "动作名需与 InputActions 一致;按键图标需在 InputDeviceIconSetSO 中配置(用 Input Icon Studio)。"); panelGo.SetActive(false); // 由 PanelStack 控制显隐 report.Add("MapPanel 已搭建(含探索进度 + 输入图标关闭提示;默认隐藏,由 UIManager PanelStack 管理)。"); return panelGo; } // ───────────────────────────────────────────────────────────────────── // 传送确认框 + 控制器 // ───────────────────────────────────────────────────────────────────── private static void BuildTeleportConfirm(GameObject mapPanelRoot, List report) { if (mapPanelRoot == null) { report.Add("MapPanel 缺失,跳过传送确认框搭建。"); return; } Transform canvas = mapPanelRoot.transform.parent ?? mapPanelRoot.transform; // 确认框(自包含,SetActive 显隐) var dialogGo = GetOrCreateChild(canvas, "TeleportConfirmDialog").gameObject; StretchFill(dialogGo.GetComponent() ?? dialogGo.AddComponent(), 0f); var dimImg = dialogGo.GetComponent() ?? dialogGo.AddComponent(); dimImg.color = new Color(0, 0, 0, 0.6f); var boxGo = GetOrCreateChild(dialogGo.transform, "Box").gameObject; var boxRect = boxGo.GetComponent() ?? boxGo.AddComponent(); boxRect.anchorMin = boxRect.anchorMax = new Vector2(0.5f, 0.5f); boxRect.pivot = new Vector2(0.5f, 0.5f); boxRect.sizeDelta = new Vector2(520, 260); var boxImg = boxGo.GetComponent() ?? boxGo.AddComponent(); boxImg.color = new Color(0.12f, 0.12f, 0.15f, 1f); var title = MakeText(boxGo.transform, "Title", "快速传送"); ((RectTransform)title.transform).anchoredPosition = new Vector2(0, 90); var body = MakeText(boxGo.transform, "Body", "传送到该地点?"); var confirmBtn = MakeButton(boxGo.transform, "ConfirmButton", "确认", new Vector2(-110, -90), out TMP_Text confirmLabel); var cancelBtn = MakeButton(boxGo.transform, "CancelButton", "取消", new Vector2( 110, -90), out TMP_Text cancelLabel); var dialog = GetOrAddComponent(dialogGo); AssignRef(dialog, "_titleText", title); AssignRef(dialog, "_bodyText", body); AssignRef(dialog, "_confirmLabel", confirmLabel); AssignRef(dialog, "_cancelLabel", cancelLabel); AssignRef(dialog, "_btnConfirm", confirmBtn); AssignRef(dialog, "_btnCancel", cancelBtn); dialogGo.SetActive(false); // 控制器:接 MapPanel 的 OnTeleportStationSelected var ctrlGo = GetOrCreateChild(canvas, "MapTeleportConfirmController").gameObject; var ctrl = GetOrAddComponent(ctrlGo); AssignRef(ctrl, "_mapPanel", mapPanelRoot.GetComponent()); AssignRef(ctrl, "_confirmDialog", dialog); report.Add("传送确认框 + MapTeleportConfirmController 已搭建并绑定 MapPanel。"); } // ───────────────────────────────────────────────────────────────────── // UIManager 登记 // ───────────────────────────────────────────────────────────────────── private static void RegisterMapPanelWithUIManager(GameObject mapPanelRoot, List report) { if (mapPanelRoot == null) return; var uiManager = Object.FindFirstObjectByType(); if (uiManager == null) { report.Add("场景中无 UIManager,未登记 PanelId.Map;请在 UIManager._panels 手动添加 {Map, MapPanel}。"); return; } var so = new SerializedObject(uiManager); var panels = so.FindProperty("_panels"); if (panels == null || !panels.isArray) { report.Add("UIManager._panels 不可写,请手动登记 PanelId.Map。"); } else { // 已登记则跳过 bool exists = false; for (int i = 0; i < panels.arraySize; i++) { var el = panels.GetArrayElementAtIndex(i); if (el.FindPropertyRelative("id").enumValueIndex == (int)PanelId.Map) { el.FindPropertyRelative("root").objectReferenceValue = mapPanelRoot; exists = true; break; } } if (!exists) { int idx = panels.arraySize; panels.arraySize = idx + 1; var el = panels.GetArrayElementAtIndex(idx); el.FindPropertyRelative("id").enumValueIndex = (int)PanelId.Map; el.FindPropertyRelative("root").objectReferenceValue = mapPanelRoot; } so.ApplyModifiedPropertiesWithoutUndo(); report.Add("已登记 UIManager._panels[Map] → MapPanel。"); } AssignAsset(uiManager, "_onMapOpen", report, false, "EVT_MapOpen", "EVT_OpenMap"); } // ───────────────────────────────────────────────────────────────────── // 通用辅助 // ───────────────────────────────────────────────────────────────────── private static void AssignMapIcons(Object target, List report) { AssignAsset(target, "_iconSavePoint", report, false, "ICN_Map_SavePoint", "ICN_SavePoint"); AssignAsset(target, "_iconBossRoom", report, false, "ICN_Map_Boss", "ICN_Boss"); AssignAsset(target, "_iconShop", report, false, "ICN_Map_Shop", "ICN_Shop"); AssignAsset(target, "_iconTeleport", report, false, "ICN_Map_Teleport", "ICN_Teleport"); } private static GameObject FindHudCanvas() { Scene scene = SceneManager.GetActiveScene(); foreach (GameObject root in scene.GetRootGameObjects()) { if (root.name == "HUD Canvas") return root; foreach (string path in new[] { "[UI]/UIRoot/HUD Canvas", "UIRoot/HUD Canvas", "HUD Canvas" }) { var found = root.transform.Find(path); if (found != null) return found.gameObject; } } return null; } private static GameObject GetOrCreateMapCanvas(string name, int sortOrder) { Scene scene = SceneManager.GetActiveScene(); Transform uiRoot = null; foreach (GameObject root in scene.GetRootGameObjects()) { if (root.name == name) return root; foreach (string path in new[] { $"[UI]/UIRoot/{name}", $"UIRoot/{name}" }) { var found = root.transform.Find(path); if (found != null) return found.gameObject; } uiRoot ??= root.transform.Find("[UI]/UIRoot") ?? root.transform.Find("UIRoot"); } var canvasGo = new GameObject(name); Undo.RegisterCreatedObjectUndo(canvasGo, $"Create {name}"); if (uiRoot != null) canvasGo.transform.SetParent(uiRoot, false); var canvas = canvasGo.AddComponent(); canvas.renderMode = RenderMode.ScreenSpaceOverlay; canvas.sortingOrder = sortOrder; var scaler = canvasGo.AddComponent(); scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; scaler.referenceResolution = new Vector2(1920, 1080); canvasGo.AddComponent(); return canvasGo; } // ── UI 元素构造 ────────────────────────────────────────────────────── private static GameObject MakeChild(Transform parent, string name, Vector2 size) { var go = GetOrCreateChild(parent, name).gameObject; var rt = go.GetComponent() ?? go.AddComponent(); rt.sizeDelta = size; return go; } private static Image MakeImage(Transform parent, string name, Color color, Vector2 size, bool raycast) { var go = MakeChild(parent, name, size); var img = go.GetComponent() ?? go.AddComponent(); img.color = color; img.raycastTarget = raycast; return img; } private static TMP_Text MakeText(Transform parent, string name, string text) { var go = MakeChild(parent, name, new Vector2(240, 40)); var t = go.GetComponent() ?? go.AddComponent(); t.text = text; t.alignment = TextAlignmentOptions.Center; t.fontSize = 28; t.raycastTarget = false; return t; } private static Button MakeButton(Transform parent, string name, string label, Vector2 anchoredPos, out TMP_Text labelText) { var go = MakeChild(parent, name, new Vector2(160, 56)); ((RectTransform)go.transform).anchorMin = ((RectTransform)go.transform).anchorMax = new Vector2(0.5f, 0.5f); ((RectTransform)go.transform).anchoredPosition = anchoredPos; var img = go.GetComponent() ?? go.AddComponent(); img.color = new Color(0.25f, 0.25f, 0.3f, 1f); var btn = GetOrAddComponent