Zabiju je
This commit is contained in:
318
Assets/Scripts/InGameHUDBuilder.cs
Normal file
318
Assets/Scripts/InGameHUDBuilder.cs
Normal file
@@ -0,0 +1,318 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
|
||||
/// <summary>
|
||||
/// 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()
|
||||
/// </summary>
|
||||
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 ──────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>Called from GameManager.OnSceneLoaded before BindClientScene.</summary>
|
||||
public void BuildNow()
|
||||
{
|
||||
if (_built) return;
|
||||
_built = true;
|
||||
Build();
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (!_built) Build(); // safety fallback
|
||||
}
|
||||
|
||||
// ── Build ─────────────────────────────────────────────────────────────────
|
||||
void Build()
|
||||
{
|
||||
var rt = GetComponent<RectTransform>();
|
||||
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<TextMeshProUGUI>();
|
||||
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<TextMeshProUGUI>();
|
||||
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<TextMeshProUGUI>();
|
||||
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<Button>();
|
||||
var recColors = recButton.colors;
|
||||
recColors.pressedColor = new Color(0.1f, 0.4f, 0.8f);
|
||||
recButton.colors = recColors;
|
||||
recButton.targetGraphic = recBg;
|
||||
recButton.onClick.AddListener(() => MapCameraController.Instance?.Recenter());
|
||||
var recTxt = AddTextChild(recBtn, "_RecTxt", "⊙", 42, FontStyles.Bold, TextAlignmentOptions.Center);
|
||||
recTxt.color = Color.white;
|
||||
}
|
||||
|
||||
void BuildActionButton(RectTransform parent)
|
||||
{
|
||||
// MUST be a DIRECT child so Transform.Find("ActionButton") works
|
||||
var btn = AddChild("ActionButton", parent);
|
||||
Anchor(btn, new Vector2(0.15f, 0f), new Vector2(0.80f, 0f));
|
||||
btn.pivot = new Vector2(0.5f, 0f);
|
||||
btn.sizeDelta = new Vector2(0f, 90f);
|
||||
btn.anchoredPosition = new Vector2(0f, 12f);
|
||||
|
||||
var bg = AddImage(btn.gameObject, C_GREEN);
|
||||
var button = btn.gameObject.AddComponent<Button>();
|
||||
var colors = button.colors;
|
||||
colors.normalColor = C_GREEN;
|
||||
colors.pressedColor = new Color(0.12f, 0.55f, 0.22f);
|
||||
button.colors = colors;
|
||||
button.targetGraphic = bg;
|
||||
|
||||
// TMP child named "Text" so GetComponentInChildren<TMP_Text> finds it
|
||||
var txtRt = AddChild("Text", btn);
|
||||
Stretch(txtRt);
|
||||
var tmp = txtRt.gameObject.AddComponent<TextMeshProUGUI>();
|
||||
tmp.text = "ACTION";
|
||||
tmp.fontSize = 44;
|
||||
tmp.fontStyle = FontStyles.Bold;
|
||||
tmp.color = Color.white;
|
||||
tmp.alignment = TextAlignmentOptions.Center;
|
||||
|
||||
btn.gameObject.SetActive(false); // hidden until proximity detected
|
||||
}
|
||||
|
||||
void BuildSabotagePanel(RectTransform parent)
|
||||
{
|
||||
// DIRECT child
|
||||
var panel = AddChild("SabotagePanel", parent);
|
||||
Anchor(panel, new Vector2(0f, 0.88f), new Vector2(1f, 1f));
|
||||
panel.offsetMin = new Vector2(0f, -10f);
|
||||
panel.offsetMax = new Vector2(0f, -80f);
|
||||
AddImage(panel.gameObject, C_RED * new Color(1, 1, 1, 0.88f));
|
||||
|
||||
var timer = AddChild("SabotageTimer", panel);
|
||||
Stretch(timer);
|
||||
var tmp = timer.gameObject.AddComponent<TextMeshProUGUI>();
|
||||
tmp.text = "SABOTAGE!";
|
||||
tmp.fontSize = 48;
|
||||
tmp.fontStyle = FontStyles.Bold;
|
||||
tmp.color = Color.white;
|
||||
tmp.alignment = TextAlignmentOptions.Center;
|
||||
|
||||
panel.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
void BuildMeetingPanel(RectTransform parent)
|
||||
{
|
||||
// DIRECT child — populated by GameManager_UI.ShowMeetingPanel at runtime
|
||||
var panel = AddChild("MeetingPanel", parent);
|
||||
Anchor(panel, new Vector2(0.05f, 0.10f), new Vector2(0.95f, 0.90f));
|
||||
AddImage(panel.gameObject, new Color(0.04f, 0.05f, 0.14f, 0.96f));
|
||||
|
||||
var title = AddChild("_MeetingTitle", panel);
|
||||
Anchor(title, new Vector2(0f, 0.85f), new Vector2(1f, 1f));
|
||||
var titleTmp = title.gameObject.AddComponent<TextMeshProUGUI>();
|
||||
titleTmp.text = "EMERGENCY MEETING";
|
||||
titleTmp.fontSize = 44;
|
||||
titleTmp.fontStyle = FontStyles.Bold;
|
||||
titleTmp.color = C_ORANGE;
|
||||
titleTmp.alignment = TextAlignmentOptions.Center;
|
||||
|
||||
panel.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
void BuildGameEndPanel(RectTransform parent)
|
||||
{
|
||||
// DIRECT child, full-screen overlay
|
||||
var panel = AddChild("GameEndPanel", parent);
|
||||
Stretch(panel);
|
||||
AddImage(panel.gameObject, new Color(0f, 0f, 0f, 0.85f));
|
||||
|
||||
var txt = AddChild("GameEndText", panel);
|
||||
Stretch(txt);
|
||||
var tmp = txt.gameObject.AddComponent<TextMeshProUGUI>();
|
||||
tmp.text = "";
|
||||
tmp.fontSize = 72;
|
||||
tmp.fontStyle = FontStyles.Bold;
|
||||
tmp.color = Color.white;
|
||||
tmp.alignment = TextAlignmentOptions.Center;
|
||||
|
||||
panel.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
RectTransform AddChild(string name, RectTransform parent)
|
||||
{
|
||||
var go = new GameObject(name);
|
||||
var rt = go.AddComponent<RectTransform>();
|
||||
rt.SetParent(parent, false);
|
||||
rt.localScale = Vector3.one;
|
||||
return rt;
|
||||
}
|
||||
|
||||
Image AddImage(GameObject go, Color color)
|
||||
{
|
||||
var img = go.AddComponent<Image>();
|
||||
img.color = color;
|
||||
return img;
|
||||
}
|
||||
|
||||
TextMeshProUGUI AddTextChild(RectTransform parent, string name, string text,
|
||||
float size, FontStyles style, TextAlignmentOptions align)
|
||||
{
|
||||
var rt = AddChild(name, parent);
|
||||
Stretch(rt);
|
||||
var tmp = rt.gameObject.AddComponent<TextMeshProUGUI>();
|
||||
tmp.text = text;
|
||||
tmp.fontSize = size;
|
||||
tmp.fontStyle = style;
|
||||
tmp.alignment = align;
|
||||
tmp.color = Color.white;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void Anchor(RectTransform rt, Vector2 min, Vector2 max)
|
||||
{
|
||||
rt.anchorMin = min;
|
||||
rt.anchorMax = max;
|
||||
rt.offsetMin = Vector2.zero;
|
||||
rt.offsetMax = Vector2.zero;
|
||||
}
|
||||
|
||||
void Stretch(RectTransform rt)
|
||||
{
|
||||
rt.anchorMin = Vector2.zero;
|
||||
rt.anchorMax = Vector2.one;
|
||||
rt.offsetMin = Vector2.zero;
|
||||
rt.offsetMax = Vector2.zero;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user