Files
megakoop/Editor/UGUIMenuBuilder.cs
2025-10-29 19:14:20 +01:00

574 lines
39 KiB
C#

#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using System.Linq;
namespace MegaKoop.EditorTools
{
public static class UGUIMenuBuilder
{
[MenuItem("Tools/MegaKoop/Generate UGUI Main Menu", priority = 0)]
public static void GenerateUGUIMainMenu()
{
// Create or use dedicated UI_Canvas (do NOT bind to a random existing Canvas)
Canvas canvas = null;
var canvasGO = GameObject.Find("UI_Canvas");
if (canvasGO == null)
{
canvasGO = new GameObject("UI_Canvas", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
Undo.RegisterCreatedObjectUndo(canvasGO, "Create Canvas");
}
canvas = canvasGO.GetComponent<Canvas>();
if (canvas == null) canvas = canvasGO.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvas.sortingOrder = 100;
var scaler = canvasGO.GetComponent<CanvasScaler>();
if (scaler == null) scaler = canvasGO.AddComponent<CanvasScaler>();
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
scaler.referenceResolution = new Vector2(1920, 1080);
scaler.matchWidthOrHeight = 0.5f;
var raycaster = canvasGO.GetComponent<GraphicRaycaster>();
if (raycaster == null) raycaster = canvasGO.AddComponent<GraphicRaycaster>();
// Ensure/upgrade EventSystem based on available modules
var existingES = Object.FindObjectOfType<UnityEngine.EventSystems.EventSystem>();
var inputSystemModuleType = System.Type.GetType("UnityEngine.InputSystem.UI.InputSystemUIInputModule, Unity.InputSystem", false);
bool wantIS = inputSystemModuleType != null;
bool wantLegacy = !wantIS;
GameObject esGO = null;
if (existingES == null)
{
var es = new GameObject("EventSystem", typeof(UnityEngine.EventSystems.EventSystem));
if (wantIS) es.AddComponent(inputSystemModuleType);
if (wantLegacy) es.AddComponent<UnityEngine.EventSystems.StandaloneInputModule>();
Undo.RegisterCreatedObjectUndo(es, "Create EventSystem");
esGO = es;
}
else
{
esGO = existingES.gameObject;
var sim = existingES.GetComponent<UnityEngine.EventSystems.StandaloneInputModule>();
var ism = inputSystemModuleType != null ? existingES.GetComponent(inputSystemModuleType) : null;
if (wantIS && ism == null) Undo.AddComponent(existingES.gameObject, inputSystemModuleType);
if (wantLegacy && sim == null) Undo.AddComponent<UnityEngine.EventSystems.StandaloneInputModule>(existingES.gameObject);
}
// Assign actionsAsset only if the picked InputActionAsset contains a "UI" action map
bool assignedUIActions = false;
if (wantIS && inputSystemModuleType != null && esGO != null)
{
var module = esGO.GetComponent(inputSystemModuleType);
if (module != null)
{
var prop = inputSystemModuleType.GetProperty("actionsAsset");
if (prop != null)
{
var current = prop.GetValue(module, null) as UnityEngine.Object;
if (current == null)
{
var iaaType = System.Type.GetType("UnityEngine.InputSystem.InputActionAsset, Unity.InputSystem", false);
string ChooseUIAsset()
{
var guids = AssetDatabase.FindAssets("t:InputActionAsset");
foreach (var g in guids)
{
var path = AssetDatabase.GUIDToAssetPath(g);
var asset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path);
if (asset != null && iaaType != null && iaaType.IsInstanceOfType(asset))
{
var map = iaaType.GetMethod("FindActionMap", new[] { typeof(string), typeof(bool) })?.Invoke(asset, new object[] { "UI", true });
if (map != null) return path;
}
}
return null;
}
var best = ChooseUIAsset();
if (!string.IsNullOrEmpty(best))
{
var asset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(best);
prop.SetValue(module, asset, null);
var comp = module as Component; if (comp != null) EditorUtility.SetDirty(comp);
assignedUIActions = true;
}
// else: leave null to let module use its default actions
}
else { assignedUIActions = true; }
}
}
}
if (esGO != null)
{
var hasSIM = esGO.GetComponent<UnityEngine.EventSystems.StandaloneInputModule>() != null;
var hasIS = inputSystemModuleType != null && esGO.GetComponent(inputSystemModuleType) != null;
if (hasIS && !assignedUIActions && !hasSIM)
{
Undo.AddComponent<UnityEngine.EventSystems.StandaloneInputModule>(esGO);
}
Debug.Log($"[UGUIMenuBuilder] EventSystem configured. InputSystem={(hasIS ? "ON" : "OFF")} (actions={(assignedUIActions ? "OK" : "none")}), Standalone={(hasSIM ? "ON" : "OFF")}");
}
// Clean old generated panels if present to avoid duplicates
void DestroyChildIfExists(Transform parent, string childName)
{
var child = parent.Find(childName);
if (child != null)
{
Undo.DestroyObjectImmediate(child.gameObject);
}
}
DestroyChildIfExists(canvas.transform, "Panel_MainMenu");
DestroyChildIfExists(canvas.transform, "Panel_Settings");
DestroyChildIfExists(canvas.transform, "Panel_Lobby");
// Root panels
var panelMain = CreatePanel(canvas.transform, "Panel_MainMenu", new Vector2(900, 800));
var panelSettings = CreatePanel(canvas.transform, "Panel_Settings", new Vector2(900, 800));
var panelLobby = CreatePanel(canvas.transform, "Panel_Lobby", new Vector2(1100, 820));
panelSettings.SetActive(false);
panelLobby.SetActive(false);
// Main Menu Content
var mainContainer = CreateVerticalGroup(panelMain.transform, "Main_VLayout", 20, TextAnchor.MiddleCenter, new RectOffset(30, 30, 30, 30));
CreateText(mainContainer.transform, "Text_Title", "MEGA KOOP", 70, TextAnchor.MiddleCenter, Color.white, FontStyles.Bold);
CreateText(mainContainer.transform, "Text_Subtitle", "CO-OP ADVENTURE", 20, TextAnchor.MiddleCenter, new Color(0.8f,0.8f,0.8f), FontStyles.Normal);
CreateSpacer(mainContainer.transform, 20);
CreateMenuButton(mainContainer.transform, "Button_Multiplayer", "MULTIPLAYER");
CreateMenuButton(mainContainer.transform, "Button_Settings", "SETTINGS");
CreateMenuButton(mainContainer.transform, "Button_Quit", "QUIT GAME", isDanger:true);
// Settings
var settingsContainer = CreateVerticalGroup(panelSettings.transform, "Settings_VLayout", 16, TextAnchor.UpperCenter, new RectOffset(30,30,30,30));
CreateText(settingsContainer.transform, "Text_SettingsTitle", "SETTINGS", 48, TextAnchor.MiddleCenter, Color.white, FontStyles.Bold);
CreateSpacer(settingsContainer.transform, 10);
var gfxGroup = CreateVerticalGroup(settingsContainer.transform, "Graphics_Group", 10, TextAnchor.UpperLeft, new RectOffset(10,10,10,10));
CreateText(gfxGroup.transform, "Text_Graphics", "Graphics", 24, TextAnchor.MiddleLeft, Color.white, FontStyles.Bold);
CreateUnityDropdown(gfxGroup.transform, "Dropdown_Quality", new string[]{"Low","Medium","High","Ultra"});
CreateToggle(gfxGroup.transform, "Toggle_Fullscreen", "Fullscreen", true);
CreateUnityDropdown(gfxGroup.transform, "Dropdown_Resolution", new string[]{"1280x720","1920x1080","2560x1440","3840x2160"});
var audioGroup = CreateVerticalGroup(settingsContainer.transform, "Audio_Group", 10, TextAnchor.UpperLeft, new RectOffset(10,10,10,10));
CreateText(audioGroup.transform, "Text_Audio", "Audio", 24, TextAnchor.MiddleLeft, Color.white, FontStyles.Bold);
CreateLabeledSlider(audioGroup.transform, "Master Volume", "Slider_MasterVolume", 0, 100, 100);
CreateLabeledSlider(audioGroup.transform, "Music Volume", "Slider_MusicVolume", 0, 100, 80);
CreateLabeledSlider(audioGroup.transform, "SFX Volume", "Slider_SFXVolume", 0, 100, 100);
var settingsButtons = CreateHorizontalGroup(settingsContainer.transform, "Settings_Buttons", 10, TextAnchor.MiddleCenter, new RectOffset(0,0,0,0));
CreateMenuButton(settingsButtons.transform, "Button_ApplySettings", "APPLY");
CreateMenuButton(settingsButtons.transform, "Button_BackFromSettings", "BACK");
// Lobby
var lobbyContainer = CreateVerticalGroup(panelLobby.transform, "Lobby_VLayout", 16, TextAnchor.UpperCenter, new RectOffset(24,24,24,24));
var lobbyHeader = CreateHorizontalGroup(lobbyContainer.transform, "Lobby_Header", 10, TextAnchor.MiddleCenter, new RectOffset(0,0,0,10));
CreateText(lobbyHeader.transform, "Text_LobbyTitle", "MULTIPLAYER LOBBY", 36, TextAnchor.MiddleLeft, new Color(0.7f,1f,0.7f), FontStyles.Bold);
CreateText(lobbyHeader.transform, "Text_Status", "OFFLINE", 18, TextAnchor.MiddleRight, new Color(0.8f,0.8f,0.8f), FontStyles.Bold);
// Lobby code area
var codeGroup = CreateVerticalGroup(lobbyContainer.transform, "Lobby_CodeGroup", 8, TextAnchor.MiddleCenter, new RectOffset(10,10,10,10));
CreateText(codeGroup.transform, "Text_LobbyCodeLabel", "LOBBY CODE", 16, TextAnchor.MiddleCenter, new Color(0.8f,0.8f,0.8f), FontStyles.Bold);
var codeRow = CreateHorizontalGroup(codeGroup.transform, "Lobby_CodeRow", 10, TextAnchor.MiddleCenter, new RectOffset(0,0,0,0));
CreateText(codeRow.transform, "Text_LobbyCodeValue", "------", 44, TextAnchor.MiddleCenter, new Color(0.7f,1f,0.7f), FontStyles.Bold);
CreateMenuButton(codeRow.transform, "Button_CopyCode", "COPY");
CreateText(codeGroup.transform, "Text_LobbyCodeHint", "Share this code with friends to invite them", 12, TextAnchor.MiddleCenter, new Color(0.8f,0.8f,0.8f), FontStyles.Normal);
// Tabs
var tabs = CreateHorizontalGroup(lobbyContainer.transform, "Lobby_Tabs", 0, TextAnchor.MiddleCenter, new RectOffset(0,0,0,0));
CreateMenuButton(tabs.transform, "Button_HostTab", "HOST LOBBY");
CreateMenuButton(tabs.transform, "Button_JoinTab", "JOIN LOBBY");
// Join content
var joinGroup = CreateVerticalGroup(lobbyContainer.transform, "Group_Join", 10, TextAnchor.UpperCenter, new RectOffset(10,10,10,10));
var joinRow = CreateHorizontalGroup(joinGroup.transform, "Join_Row", 10, TextAnchor.MiddleCenter, new RectOffset(0,0,0,0));
CreateInputField(joinRow.transform, "Input_LobbyCode", "Enter 6-digit code...");
CreateMenuButton(joinGroup.transform, "Button_Connect", "CONNECT");
// Host content
var hostGroup = CreateVerticalGroup(lobbyContainer.transform, "Group_Host", 10, TextAnchor.UpperCenter, new RectOffset(10,10,10,10));
CreateDropdown(hostGroup.transform, "Dropdown_MaxPlayers", new string[]{"2","3","4","8"});
CreateToggle(hostGroup.transform, "Toggle_PublicLobby", "Public", true);
CreateMenuButton(hostGroup.transform, "Button_CreateLobby", "CREATE LOBBY");
// Players list
var playersHeader = CreateHorizontalGroup(lobbyContainer.transform, "Players_Header", 10, TextAnchor.MiddleLeft, new RectOffset(0,0,0,0));
CreateText(playersHeader.transform, "Text_PlayersTitle", "PLAYERS", 20, TextAnchor.MiddleLeft, Color.white, FontStyles.Bold);
CreateText(playersHeader.transform, "Text_PlayerCount", "0/4", 18, TextAnchor.MiddleRight, new Color(0.7f,1f,0.7f), FontStyles.Bold);
var scroll = CreateScrollList(lobbyContainer.transform, out var content, "Scroll_Players", "Viewport", "Content_PlayersList");
var empty = CreateText(lobbyContainer.transform, "Empty_Players", "Waiting for players...", 16, TextAnchor.MiddleCenter, new Color(0.8f,0.8f,0.8f), FontStyles.Italic);
// Template for player entry
var template = new GameObject("PlayerItemTemplate", typeof(RectTransform), typeof(HorizontalLayoutGroup));
template.transform.SetParent(content, false);
var hlg = template.GetComponent<HorizontalLayoutGroup>();
hlg.childAlignment = TextAnchor.MiddleLeft; hlg.spacing = 12; hlg.childControlWidth = false; hlg.childForceExpandWidth = true;
var avatar = CreateImage(template.transform, "Image_Avatar", new Color(0.3f,0.6f,0.3f));
var name = CreateText(template.transform, "Text_PlayerName", "Player", 18, TextAnchor.MiddleLeft, Color.white, FontStyles.Bold);
var status = CreateText(template.transform, "Text_PlayerStatus", "NOT READY", 14, TextAnchor.MiddleRight, new Color(0.8f,0.8f,0.8f), FontStyles.Bold);
((RectTransform)avatar.transform).sizeDelta = new Vector2(40,40);
template.SetActive(false);
// Host controls
var hostControls = CreateHorizontalGroup(lobbyContainer.transform, "Host_Controls", 10, TextAnchor.MiddleCenter, new RectOffset(0,0,0,0));
CreateMenuButton(hostControls.transform, "Button_InviteFriends", "INVITE FRIENDS");
CreateMenuButton(hostControls.transform, "Button_KickSelected", "KICK SELECTED");
// Friends picker will be a modal overlay under Panel_Lobby (so it doesn't stretch main layout)
var friendsOverlay = new GameObject("Panel_Friends", typeof(RectTransform), typeof(Image));
friendsOverlay.transform.SetParent(panelLobby.transform, false);
var ovRT = (RectTransform)friendsOverlay.transform; ovRT.anchorMin = new Vector2(0,0); ovRT.anchorMax = new Vector2(1,1); ovRT.offsetMin = Vector2.zero; ovRT.offsetMax = Vector2.zero;
var ovImg = friendsOverlay.GetComponent<Image>(); ovImg.color = new Color(0,0,0,0.55f);
friendsOverlay.SetActive(false);
// Transparent background button to close on backdrop click
var closeBg = new GameObject("Button_CloseFriendsOverlay", typeof(RectTransform), typeof(Image), typeof(Button));
closeBg.transform.SetParent(friendsOverlay.transform, false);
var closeBgRT = (RectTransform)closeBg.transform; closeBgRT.anchorMin = new Vector2(0,0); closeBgRT.anchorMax = new Vector2(1,1); closeBgRT.offsetMin = Vector2.zero; closeBgRT.offsetMax = Vector2.zero;
var closeBgImg = closeBg.GetComponent<Image>(); closeBgImg.color = new Color(0,0,0,0);
// Center modal window
var friendsWindow = CreatePanel(friendsOverlay.transform, "Friends_Window", new Vector2(900, 420));
var friendsV = CreateVerticalGroup(friendsWindow.transform, "Friends_VLayout", 8, TextAnchor.UpperCenter, new RectOffset(16,16,16,16));
CreateText(friendsV.transform, "Text_FriendsTitle", "INVITE FRIENDS", 24, TextAnchor.MiddleCenter, Color.white, FontStyles.Bold);
CreateText(friendsV.transform, "Text_FriendsHint", "Select a friend to send a Steam invite.", 12, TextAnchor.MiddleCenter, new Color(0.8f,0.8f,0.8f), FontStyles.Normal);
var friendsScroll = CreateScrollList(friendsV.transform, out var friendsContent, "Scroll_Friends", "Viewport", "Content_FriendsList");
// Make friends content a compact grid of icons
var vlgFriends = friendsContent.GetComponent<VerticalLayoutGroup>();
if (vlgFriends) Object.DestroyImmediate(vlgFriends);
var gridFriends = friendsContent.gameObject.AddComponent<GridLayoutGroup>();
gridFriends.cellSize = new Vector2(72, 72);
gridFriends.spacing = new Vector2(10, 10);
gridFriends.startAxis = GridLayoutGroup.Axis.Horizontal;
var csfFriends = friendsContent.GetComponent<ContentSizeFitter>();
if (csfFriends == null) csfFriends = friendsContent.gameObject.AddComponent<ContentSizeFitter>();
csfFriends.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
CreateText(friendsV.transform, "Empty_Friends", "No friends found.", 14, TextAnchor.MiddleCenter, new Color(0.8f,0.8f,0.8f), FontStyles.Italic);
var friendsFooter = CreateHorizontalGroup(friendsV.transform, "Friends_Footer", 8, TextAnchor.MiddleCenter, new RectOffset(0,0,0,0));
CreateMenuButton(friendsFooter.transform, "Button_BackFromFriends", "BACK");
// Ready
CreateMenuButton(lobbyContainer.transform, "Button_ToggleReady", "TOGGLE READY");
// Footer
var footer = CreateHorizontalGroup(lobbyContainer.transform, "Lobby_Footer", 10, TextAnchor.MiddleCenter, new RectOffset(0,0,0,0));
CreateMenuButton(footer.transform, "Button_StartGame", "START GAME");
CreateMenuButton(footer.transform, "Button_LeaveLobby", "LEAVE LOBBY");
CreateMenuButton(footer.transform, "Button_BackFromLobby", "BACK TO MENU");
// Controllers
var mainCtrl = canvas.gameObject.GetComponent<MegaKoop.UI.UGUIMainMenuController>();
if (mainCtrl == null) mainCtrl = canvas.gameObject.AddComponent<MegaKoop.UI.UGUIMainMenuController>();
var lobbyCtrl = canvas.gameObject.GetComponent<MegaKoop.UI.UGUIMultiplayerLobbyController>();
if (lobbyCtrl == null) lobbyCtrl = canvas.gameObject.AddComponent<MegaKoop.UI.UGUIMultiplayerLobbyController>();
// Inject panel references for reliability
mainCtrl.SetPanels(panelMain, panelSettings, panelLobby);
lobbyCtrl.SetLobbyRoot(panelLobby);
Selection.activeGameObject = canvas.gameObject;
Debug.Log("[UGUIMenuBuilder] UGUI Main Menu generated successfully. You can press Play and test.");
}
private static GameObject CreatePanel(Transform parent, string name, Vector2 size)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Image));
go.transform.SetParent(parent, false);
var rt = (RectTransform)go.transform;
rt.anchorMin = rt.anchorMax = new Vector2(0.5f, 0.5f);
rt.pivot = new Vector2(0.5f, 0.5f);
rt.sizeDelta = size;
var img = go.GetComponent<Image>();
img.color = new Color(0.13f, 0.13f, 0.13f, 0.95f);
return go;
}
private static GameObject CreateUnityDropdown(Transform parent, string name, string[] options)
{
var resources = new DefaultControls.Resources();
var go = DefaultControls.CreateDropdown(resources);
go.name = name;
go.transform.SetParent(parent, false);
var rt = (RectTransform)go.transform; rt.sizeDelta = new Vector2(600, 50);
var font = Resources.GetBuiltinResource<Font>("Arial.ttf");
foreach (var t in go.GetComponentsInChildren<Text>(true)) t.font = font;
var dd = go.GetComponent<Dropdown>();
dd.options = (options != null ? options.Select(o => new Dropdown.OptionData(o)).ToList() : new System.Collections.Generic.List<Dropdown.OptionData>());
dd.RefreshShownValue();
return go;
}
private static GameObject CreateVerticalGroup(Transform parent, string name, float spacing, TextAnchor align, RectOffset padding)
{
var go = new GameObject(name, typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter));
go.transform.SetParent(parent, false);
var rt = (RectTransform)go.transform;
rt.anchorMin = new Vector2(0, 0); rt.anchorMax = new Vector2(1, 1); rt.offsetMin = new Vector2(0,0); rt.offsetMax = new Vector2(0,0);
var vlg = go.GetComponent<VerticalLayoutGroup>();
vlg.spacing = spacing; vlg.childAlignment = align; vlg.padding = padding; vlg.childForceExpandWidth = true; vlg.childControlWidth = true; vlg.childControlHeight = false;
var fitter = go.GetComponent<ContentSizeFitter>();
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize; fitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
return go;
}
private static GameObject CreateHorizontalGroup(Transform parent, string name, float spacing, TextAnchor align, RectOffset padding)
{
var go = new GameObject(name, typeof(RectTransform), typeof(HorizontalLayoutGroup));
go.transform.SetParent(parent, false);
var rt = (RectTransform)go.transform;
rt.anchorMin = new Vector2(0, 0); rt.anchorMax = new Vector2(1, 0); rt.pivot = new Vector2(0.5f, 0.5f);
rt.sizeDelta = new Vector2(0, 60);
var hlg = go.GetComponent<HorizontalLayoutGroup>();
hlg.spacing = spacing; hlg.childAlignment = align; hlg.padding = padding; hlg.childControlWidth = true; hlg.childForceExpandWidth = true;
return go;
}
private static GameObject CreateText(Transform parent, string name, string text, int fontSize, TextAnchor anchor, Color color, FontStyles fontStyle)
{
var go = new GameObject(name, typeof(RectTransform), typeof(TextMeshProUGUI), typeof(LayoutElement));
go.transform.SetParent(parent, false);
var t = go.GetComponent<TextMeshProUGUI>();
t.text = text; t.fontSize = fontSize; t.color = color; t.alignment = MapAlignment(anchor); t.fontStyle = fontStyle;
t.enableWordWrapping = false; // avoid vertical letters
t.raycastTarget = false; // don't block parent UI clicks
if (TMP_Settings.defaultFontAsset != null) t.font = TMP_Settings.defaultFontAsset;
var le = go.GetComponent<LayoutElement>();
le.minHeight = Mathf.Max(24, fontSize + 12);
var rt = (RectTransform)go.transform; rt.sizeDelta = new Vector2(0, fontSize + 20);
return go;
}
private static TextAlignmentOptions MapAlignment(TextAnchor anchor)
{
switch (anchor)
{
case TextAnchor.UpperLeft: return TextAlignmentOptions.TopLeft;
case TextAnchor.UpperCenter: return TextAlignmentOptions.Top;
case TextAnchor.UpperRight: return TextAlignmentOptions.TopRight;
case TextAnchor.MiddleLeft: return TextAlignmentOptions.MidlineLeft;
case TextAnchor.MiddleCenter: return TextAlignmentOptions.Midline;
case TextAnchor.MiddleRight: return TextAlignmentOptions.MidlineRight;
case TextAnchor.LowerLeft: return TextAlignmentOptions.BottomLeft;
case TextAnchor.LowerCenter: return TextAlignmentOptions.Bottom;
case TextAnchor.LowerRight: return TextAlignmentOptions.BottomRight;
default: return TextAlignmentOptions.Center;
}
}
private static GameObject CreateMenuButton(Transform parent, string name, string label, bool isDanger = false)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Image), typeof(Button));
go.transform.SetParent(parent, false);
var img = go.GetComponent<Image>();
img.color = isDanger ? new Color(0.5f, 0.15f, 0.15f, 0.9f) : new Color(0.25f, 0.5f, 0.25f, 0.9f);
var rt = (RectTransform)go.transform; rt.sizeDelta = new Vector2(0, 48);
var leBtn = go.AddComponent<LayoutElement>();
leBtn.flexibleWidth = 1; leBtn.minHeight = 48;
var text = CreateText(go.transform, "Text", label, 20, TextAnchor.MiddleCenter, Color.white, FontStyles.Bold);
var textRT = (RectTransform)text.transform; textRT.anchorMin = new Vector2(0,0); textRT.anchorMax = new Vector2(1,1); textRT.offsetMin = Vector2.zero; textRT.offsetMax = Vector2.zero;
return go;
}
private static GameObject CreateInputField(Transform parent, string name, string placeholder)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Image), typeof(TMP_InputField));
go.transform.SetParent(parent, false);
var img = go.GetComponent<Image>(); img.color = new Color(0.1f,0.1f,0.1f,0.9f);
var rt = (RectTransform)go.transform; rt.sizeDelta = new Vector2(600, 50);
var viewport = new GameObject("TextViewport", typeof(RectTransform)); viewport.transform.SetParent(go.transform, false);
var viewportRT = (RectTransform)viewport.transform; viewportRT.anchorMin = new Vector2(0,0); viewportRT.anchorMax = new Vector2(1,1); viewportRT.offsetMin = new Vector2(10,6); viewportRT.offsetMax = new Vector2(-10,-6);
var text = CreateText(viewport.transform, "Text", string.Empty, 20, TextAnchor.MiddleLeft, Color.white, FontStyles.Normal);
var placeholderGO = CreateText(viewport.transform, "Placeholder", placeholder, 18, TextAnchor.MiddleLeft, new Color(0.7f,0.7f,0.7f), FontStyles.Italic);
var input = go.GetComponent<TMP_InputField>();
input.textViewport = viewportRT;
input.textComponent = text.GetComponent<TextMeshProUGUI>();
input.placeholder = placeholderGO.GetComponent<TextMeshProUGUI>();
var tRT = (RectTransform)text.transform; tRT.anchorMin = new Vector2(0,0); tRT.anchorMax = new Vector2(1,1); tRT.offsetMin = new Vector2(10,0); tRT.offsetMax = new Vector2(-10,0);
var pRT = (RectTransform)placeholderGO.transform; pRT.anchorMin = new Vector2(0,0); pRT.anchorMax = new Vector2(1,1); pRT.offsetMin = new Vector2(10,0); pRT.offsetMax = new Vector2(-10,0);
return go;
}
private static GameObject CreateDropdown(Transform parent, string name, string[] options)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Image), typeof(TMP_Dropdown));
go.transform.SetParent(parent, false);
var img = go.GetComponent<Image>(); img.color = new Color(0.2f,0.2f,0.2f,0.9f);
var rt = (RectTransform)go.transform; rt.sizeDelta = new Vector2(600, 50);
// Caption label
var caption = CreateText(go.transform, "Label", options.FirstOrDefault() ?? "Option", 18, TextAnchor.MiddleLeft, Color.white, FontStyles.Normal);
var arrow = CreateText(go.transform, "Arrow", "▼", 18, TextAnchor.MiddleRight, Color.white, FontStyles.Bold);
// Template (disabled by default)
var template = new GameObject("Template", typeof(RectTransform), typeof(Image), typeof(ScrollRect));
template.transform.SetParent(go.transform, false);
var templateRT = (RectTransform)template.transform;
templateRT.anchorMin = new Vector2(0, 0); templateRT.anchorMax = new Vector2(1, 0); templateRT.pivot = new Vector2(0.5f, 1);
templateRT.sizeDelta = new Vector2(0, 220); // visible dropdown height
template.SetActive(false);
var templateImg = template.GetComponent<Image>(); templateImg.color = new Color(0.1f,0.1f,0.1f,0.95f);
var tplCanvas = template.GetComponent<Canvas>();
if (tplCanvas == null) tplCanvas = template.AddComponent<Canvas>();
tplCanvas.overrideSorting = true;
tplCanvas.sortingOrder = 5000;
if (!template.GetComponent<GraphicRaycaster>()) template.AddComponent<GraphicRaycaster>();
// Viewport
var viewport = new GameObject("Viewport", typeof(RectTransform), typeof(Mask), typeof(Image));
viewport.transform.SetParent(template.transform, false);
var viewportRT = (RectTransform)viewport.transform; viewportRT.anchorMin = new Vector2(0,0); viewportRT.anchorMax = new Vector2(1,1); viewportRT.offsetMin = Vector2.zero; viewportRT.offsetMax = Vector2.zero;
var vImg = viewport.GetComponent<Image>(); vImg.color = new Color(0,0,0,0.2f); vImg.raycastTarget = false;
var mask = viewport.GetComponent<Mask>(); mask.showMaskGraphic = false;
// Scroll content
var content = new GameObject("Content", typeof(RectTransform), typeof(VerticalLayoutGroup));
content.transform.SetParent(viewport.transform, false);
var contentRT = (RectTransform)content.transform; contentRT.anchorMin = new Vector2(0,1); contentRT.anchorMax = new Vector2(1,1); contentRT.pivot = new Vector2(0.5f,1);
var vlg = content.GetComponent<VerticalLayoutGroup>(); vlg.spacing = 4; vlg.childForceExpandWidth = true; vlg.childControlHeight = true;
// Item (Toggle)
var item = new GameObject("Item", typeof(RectTransform), typeof(Toggle));
item.transform.SetParent(content.transform, false);
var itemRT = (RectTransform)item.transform; itemRT.sizeDelta = new Vector2(0, 30);
var itemBG = new GameObject("Item Background", typeof(RectTransform), typeof(Image));
itemBG.transform.SetParent(item.transform, false);
var itemBGImg = itemBG.GetComponent<Image>(); itemBGImg.color = new Color(0.2f,0.2f,0.2f,0.9f);
var itemCheck = new GameObject("Item Checkmark", typeof(RectTransform), typeof(Image));
itemCheck.transform.SetParent(itemBG.transform, false);
var itemCheckImg = itemCheck.GetComponent<Image>(); itemCheckImg.color = new Color(0.7f,1f,0.7f,1f);
var itemLabelGO = CreateText(item.transform, "Item Label", "Option", 18, TextAnchor.MiddleLeft, Color.white, FontStyles.Normal);
// Position sub-elements
var bgRT = (RectTransform)itemBG.transform; bgRT.anchorMin = new Vector2(0,0); bgRT.anchorMax = new Vector2(1,1); bgRT.offsetMin = Vector2.zero; bgRT.offsetMax = Vector2.zero;
var ckRT = (RectTransform)itemCheck.transform; ckRT.anchorMin = new Vector2(0,0.5f); ckRT.anchorMax = new Vector2(0,0.5f); ckRT.pivot = new Vector2(0,0.5f); ckRT.sizeDelta = new Vector2(18,18); ckRT.anchoredPosition = new Vector2(6,0);
var itemLabelRT = (RectTransform)itemLabelGO.transform; itemLabelRT.anchorMin = new Vector2(0,0); itemLabelRT.anchorMax = new Vector2(1,1); itemLabelRT.offsetMin = new Vector2(28,0); itemLabelRT.offsetMax = new Vector2(-6,0);
// Toggle wiring
var tgl = item.GetComponent<Toggle>();
tgl.targetGraphic = itemBGImg; tgl.graphic = itemCheckImg;
// Dropdown wiring
var dd = go.GetComponent<TMP_Dropdown>();
dd.template = template.GetComponent<RectTransform>();
dd.captionText = caption.GetComponent<TextMeshProUGUI>();
dd.itemText = itemLabelGO.GetComponent<TextMeshProUGUI>();
dd.options = options.Select(o => new TMP_Dropdown.OptionData(o)).ToList();
dd.alphaFadeSpeed = 0.15f;
dd.RefreshShownValue();
// ScrollRect wiring
var scr = template.GetComponent<ScrollRect>();
scr.viewport = viewport.GetComponent<RectTransform>();
scr.content = content.GetComponent<RectTransform>();
scr.horizontal = false; scr.vertical = true;
return go;
}
private static GameObject CreateSlider(Transform parent, string name, float min, float max, float value)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Slider));
go.transform.SetParent(parent, false);
var slider = go.GetComponent<Slider>();
slider.minValue = min; slider.maxValue = max; slider.value = value; slider.direction = Slider.Direction.LeftToRight; slider.wholeNumbers = true;
var rt = (RectTransform)go.transform; rt.sizeDelta = new Vector2(600, 30);
var background = new GameObject("Background", typeof(RectTransform), typeof(Image));
background.transform.SetParent(go.transform, false);
var bgRT = (RectTransform)background.transform; bgRT.anchorMin = new Vector2(0,0.25f); bgRT.anchorMax = new Vector2(1,0.75f); bgRT.offsetMin = Vector2.zero; bgRT.offsetMax = Vector2.zero;
var bgImg = background.GetComponent<Image>(); bgImg.color = new Color(0.1f,0.1f,0.1f,1f);
var fillArea = new GameObject("Fill Area", typeof(RectTransform));
fillArea.transform.SetParent(go.transform, false);
var faRT = (RectTransform)fillArea.transform; faRT.anchorMin = new Vector2(0,0.25f); faRT.anchorMax = new Vector2(1,0.75f); faRT.offsetMin = new Vector2(10,0); faRT.offsetMax = new Vector2(-10,0);
var fill = new GameObject("Fill", typeof(RectTransform), typeof(Image));
fill.transform.SetParent(fillArea.transform, false);
var fillImg = fill.GetComponent<Image>(); fillImg.color = new Color(0.4f,0.9f,0.4f,1f);
var fillRT = (RectTransform)fill.transform; fillRT.anchorMin = new Vector2(0,0); fillRT.anchorMax = new Vector2(1,1); fillRT.offsetMin = Vector2.zero; fillRT.offsetMax = Vector2.zero;
var handleArea = new GameObject("Handle Slide Area", typeof(RectTransform));
handleArea.transform.SetParent(go.transform, false);
var haRT = (RectTransform)handleArea.transform; haRT.anchorMin = new Vector2(0,0); haRT.anchorMax = new Vector2(1,1); haRT.offsetMin = Vector2.zero; haRT.offsetMax = Vector2.zero;
var handle = new GameObject("Handle", typeof(RectTransform), typeof(Image));
handle.transform.SetParent(handleArea.transform, false);
var handleImg = handle.GetComponent<Image>(); handleImg.color = new Color(0.8f,1f,0.8f,1f);
var hRT = (RectTransform)handle.transform; hRT.sizeDelta = new Vector2(16, 24); hRT.anchorMin = new Vector2(0.5f,0.5f); hRT.anchorMax = new Vector2(0.5f,0.5f); hRT.anchoredPosition = Vector2.zero;
slider.fillRect = fill.GetComponent<RectTransform>();
slider.handleRect = handle.GetComponent<RectTransform>();
slider.targetGraphic = handleImg;
slider.direction = Slider.Direction.LeftToRight;
return go;
}
private static GameObject CreateLabeledSlider(Transform parent, string label, string name, float min, float max, float value)
{
var group = CreateVerticalGroup(parent, name + "_Group", 4, TextAnchor.UpperLeft, new RectOffset(0,0,0,0));
CreateText(group.transform, name + "_Label", label, 18, TextAnchor.MiddleLeft, Color.white, FontStyles.Normal);
CreateSlider(group.transform, name, min, max, value);
return group;
}
private static GameObject CreateToggle(Transform parent, string name, string labelText, bool initial)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Toggle));
go.transform.SetParent(parent, false);
var bg = CreateImage(go.transform, "Background", new Color(0.2f,0.2f,0.2f));
var check = CreateImage(bg.transform, "Checkmark", new Color(0.7f,1f,0.7f));
var label = CreateText(go.transform, "Label", labelText, 18, TextAnchor.MiddleLeft, Color.white, FontStyles.Normal);
var t = go.GetComponent<Toggle>();
t.isOn = initial; t.graphic = check.GetComponent<Image>(); t.targetGraphic = bg.GetComponent<Image>();
var rt = (RectTransform)go.transform; rt.sizeDelta = new Vector2(600, 40);
var bgRT = (RectTransform)bg.transform; bgRT.anchorMin = new Vector2(0,0.5f); bgRT.anchorMax = new Vector2(0,0.5f); bgRT.pivot = new Vector2(0,0.5f); bgRT.sizeDelta = new Vector2(24,24);
var checkRT = (RectTransform)check.transform; checkRT.anchorMin = checkRT.anchorMax = new Vector2(0.5f,0.5f); checkRT.sizeDelta = new Vector2(16,16);
var labelRT = (RectTransform)label.transform; labelRT.anchorMin = new Vector2(0,0); labelRT.anchorMax = new Vector2(1,1); labelRT.offsetMin = new Vector2(34,0); labelRT.offsetMax = new Vector2(0,0);
return go;
}
private static GameObject CreateImage(Transform parent, string name, Color color)
{
var go = new GameObject(name, typeof(RectTransform), typeof(Image));
go.transform.SetParent(parent, false);
var img = go.GetComponent<Image>(); img.color = color;
return go;
}
private static void CreateSpacer(Transform parent, float height)
{
var sp = new GameObject("Spacer", typeof(RectTransform));
sp.transform.SetParent(parent, false);
var rt = (RectTransform)sp.transform; rt.sizeDelta = new Vector2(0, height);
}
private static ScrollRect CreateScrollList(Transform parent, out Transform content, string scrollName, string viewportName, string contentName)
{
var scrollGO = new GameObject(scrollName, typeof(RectTransform), typeof(Image), typeof(ScrollRect));
scrollGO.transform.SetParent(parent, false);
var srt = (RectTransform)scrollGO.transform; srt.sizeDelta = new Vector2(0, 240);
var img = scrollGO.GetComponent<Image>(); img.color = new Color(0,0,0,0.3f);
var viewport = new GameObject(viewportName, typeof(RectTransform), typeof(Mask), typeof(Image));
viewport.transform.SetParent(scrollGO.transform, false);
var vImg = viewport.GetComponent<Image>(); vImg.color = new Color(0,0,0,0.1f);
viewport.GetComponent<Mask>().showMaskGraphic = false;
var vprt = (RectTransform)viewport.transform; vprt.anchorMin = new Vector2(0,0); vprt.anchorMax = new Vector2(1,1); vprt.offsetMin = Vector2.zero; vprt.offsetMax = Vector2.zero;
var contentGO = new GameObject(contentName, typeof(RectTransform), typeof(VerticalLayoutGroup));
contentGO.transform.SetParent(viewport.transform, false);
var vlg = contentGO.GetComponent<VerticalLayoutGroup>(); vlg.spacing = 8; vlg.childAlignment = TextAnchor.UpperLeft; vlg.childForceExpandWidth = true;
var crt = (RectTransform)contentGO.transform; crt.anchorMin = new Vector2(0,1); crt.anchorMax = new Vector2(1,1); crt.pivot = new Vector2(0.5f,1); crt.offsetMin = new Vector2(0,0); crt.offsetMax = new Vector2(0,0);
var csf = contentGO.AddComponent<ContentSizeFitter>(); csf.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
content = contentGO.transform;
var scroll = scrollGO.GetComponent<ScrollRect>(); scroll.viewport = (RectTransform)viewport.transform; scroll.content = (RectTransform)content; scroll.horizontal = false; scroll.vertical = true;
return scroll;
}
}
}
#endif