using UnityEngine; using UnityEngine.UI; using TMPro; /// /// Programmatically builds the complete in-game HUD inside the InGame canvas (Client.unity). /// /// Call BuildNow() explicitly from GameManager.OnSceneLoaded BEFORE BindClientScene(), /// so that GameManager_UI can find the newly created elements by name. /// /// Named GameObjects created as direct children of InGame canvas (required by Transform.Find): /// • ActionButton — Button + TMP child; shown/hidden by GameManager_Tasks.UpdateProximity() /// • SabotagePanel — warning banner at top (contains "SabotageTimer" TMP_Text) /// • MeetingPanel — voting/meeting overlay (populated by GameManager_UI.ShowMeetingPanel) /// • GameEndPanel — end-of-game overlay (contains "GameEndText" TMP_Text) /// /// Named TMP_Text descendants (found by GameManager_UI.FindTMP — any depth): /// • KillCooldown — shown to impostors during kill cooldown /// • TaskList — crewmate task names /// • TaskProgress — global task completion "X/Y tasks" /// • SabotageTimer — countdown inside SabotagePanel /// • GameEndText — win/lose result text inside GameEndPanel /// • (Role already exists in the scene) /// /// Additional elements managed by this script: /// • RecenterBtn — calls MapCameraController.Instance.Recenter() /// public class InGameHUDBuilder : MonoBehaviour { // ── Color palette ───────────────────────────────────────────────────────── private static readonly Color C_BG = new Color(0.05f, 0.06f, 0.12f, 0.80f); private static readonly Color C_BAR_BG = new Color(0.03f, 0.04f, 0.08f, 0.90f); private static readonly Color C_ACCENT = new Color(0.20f, 0.60f, 1.00f, 1.00f); private static readonly Color C_GREEN = new Color(0.18f, 0.75f, 0.30f, 1.00f); private static readonly Color C_RED = new Color(0.76f, 0.19f, 0.19f, 1.00f); private static readonly Color C_ORANGE = new Color(0.95f, 0.55f, 0.10f, 1.00f); private bool _built; // ── Entry points ────────────────────────────────────────────────────────── /// Called from GameManager.OnSceneLoaded before BindClientScene. public void BuildNow() { if (_built) return; _built = true; Build(); } void Start() { if (!_built) Build(); // safety fallback } // ── Build ───────────────────────────────────────────────────────────────── void Build() { var rt = GetComponent(); if (rt == null) return; // ── Top bar: role is already in scene, add kill-cooldown ───────────── BuildTopBar(rt); // ── Right task panel ────────────────────────────────────────────────── BuildTaskPanel(rt); // ── Task progress (above bottom bar) ───────────────────────────────── BuildTaskProgress(rt); // ── Bottom bar: action button + recenter ────────────────────────────── BuildBottomBar(rt); // ── Action button (DIRECT child — Transform.Find requirement) ───────── BuildActionButton(rt); // ── Sabotage panel (DIRECT child) ───────────────────────────────────── BuildSabotagePanel(rt); // ── Meeting panel (DIRECT child) ────────────────────────────────────── BuildMeetingPanel(rt); // ── Game-end panel (DIRECT child) ───────────────────────────────────── BuildGameEndPanel(rt); } // ── Section builders ────────────────────────────────────────────────────── void BuildTopBar(RectTransform parent) { // Thin semi-transparent header at very top var bar = AddChild("_TopBar", parent); Anchor(bar, new Vector2(0f, 1f), new Vector2(1f, 1f)); bar.sizeDelta = new Vector2(0f, 90f); bar.anchoredPosition = new Vector2(0f, 0f); bar.pivot = new Vector2(0.5f, 1f); AddImage(bar.gameObject, C_BAR_BG); // Kill cooldown (right side) — starts hidden var cd = AddChild("KillCooldown", bar); Anchor(cd, new Vector2(0.5f, 0f), new Vector2(1f, 1f)); cd.offsetMin = new Vector2(0f, 6f); cd.offsetMax = new Vector2(-12f, -6f); var cdTmp = cd.gameObject.AddComponent(); cdTmp.text = ""; cdTmp.fontSize = 32; cdTmp.color = C_ORANGE; cdTmp.fontStyle = FontStyles.Bold; cdTmp.alignment = TextAlignmentOptions.MidlineRight; cd.gameObject.SetActive(false); } void BuildTaskPanel(RectTransform parent) { // Right-side floating panel (always visible during game) var panel = AddChild("_TaskPanel", parent); Anchor(panel, new Vector2(1f, 0.35f), new Vector2(1f, 0.88f)); panel.pivot = new Vector2(1f, 0.5f); panel.sizeDelta = new Vector2(280f, 0f); panel.anchoredPosition = Vector2.zero; AddImage(panel.gameObject, C_BG); // "MY TASKS" header var hdr = AddChild("_Header", panel); Anchor(hdr, new Vector2(0f, 1f), new Vector2(1f, 1f)); hdr.pivot = new Vector2(0.5f, 1f); hdr.sizeDelta = new Vector2(0f, 44f); hdr.anchoredPosition = Vector2.zero; AddImage(hdr.gameObject, C_ACCENT * new Color(1, 1, 1, 0.6f)); var hdrTmp = AddTextChild(hdr, "_HeaderTxt", "MY TASKS", 26, FontStyles.Bold, TextAlignmentOptions.Center); hdrTmp.color = Color.white; // Task list body var body = AddChild("TaskList", panel); Anchor(body, new Vector2(0f, 0f), new Vector2(1f, 1f)); body.offsetMin = new Vector2(8f, 8f); body.offsetMax = new Vector2(-8f, -48f); var taskTmp = body.gameObject.AddComponent(); taskTmp.text = ""; taskTmp.fontSize = 22; taskTmp.color = Color.white; taskTmp.alignment = TextAlignmentOptions.TopLeft; } void BuildTaskProgress(RectTransform parent) { var prog = AddChild("TaskProgress", parent); Anchor(prog, new Vector2(0f, 0f), new Vector2(1f, 0f)); prog.pivot = new Vector2(0.5f, 0f); prog.sizeDelta = new Vector2(-20f, 40f); prog.anchoredPosition = new Vector2(0f, 120f); // above bottom bar var tmp = prog.gameObject.AddComponent(); tmp.text = ""; tmp.fontSize = 28; tmp.color = Color.white; tmp.fontStyle = FontStyles.Bold; tmp.alignment = TextAlignmentOptions.Center; } void BuildBottomBar(RectTransform parent) { var bar = AddChild("_BottomBar", parent); Anchor(bar, new Vector2(0f, 0f), new Vector2(1f, 0f)); bar.pivot = new Vector2(0.5f, 0f); bar.sizeDelta = new Vector2(0f, 110f); bar.anchoredPosition = Vector2.zero; AddImage(bar.gameObject, C_BAR_BG); // Recenter button (bottom-right of bar) var recBtn = AddChild("_RecenterBtn", bar); Anchor(recBtn, new Vector2(0.82f, 0.08f), new Vector2(0.98f, 0.92f)); var recBg = AddImage(recBtn.gameObject, C_ACCENT); var recButton = recBtn.gameObject.AddComponent