// Assets/_Project/Scripts/UI/BuildProgressBar.cs using UnityEngine; using UnityEngine.UI; using TD.Gameplay; namespace TD.UI { // Local (non-networked) world-space progress bar that tracks a BuildSiteVisual. // Visible while Constructing (green) or Paused (yellow). Hidden while Queued. // Billboards to face Camera.main each LateUpdate. // Destroyed automatically when its parent BuildSiteVisual is despawned. public class BuildProgressBar : MonoBehaviour { private BuildSiteVisual source; private GameObject canvasGO; private Image fillImage; private const float BarWorldWidth = 1.8f; private const float BarWorldHeight = 0.15f; private const float HeightAboveSite = 1.5f; private static readonly Color ColorConstructing = new Color(0.15f, 0.85f, 0.15f, 1f); private static readonly Color ColorPaused = new Color(0.90f, 0.75f, 0.10f, 1f); public void Initialize(BuildSiteVisual visual) { source = visual; BuildHierarchy(); } private void BuildHierarchy() { // World-space Canvas — 100 canvas units = 1 world unit via localScale 0.01. canvasGO = new GameObject("Canvas"); canvasGO.transform.SetParent(transform, false); var canvas = canvasGO.AddComponent(); canvas.renderMode = RenderMode.WorldSpace; canvas.sortingOrder = 10; var rt = (RectTransform)canvasGO.transform; rt.sizeDelta = new Vector2(BarWorldWidth * 100f, BarWorldHeight * 100f); rt.localPosition = new Vector3(0f, HeightAboveSite, 0f); rt.localScale = Vector3.one * 0.01f; // Background var bgGO = new GameObject("Background"); bgGO.transform.SetParent(canvasGO.transform, false); var bgImg = bgGO.AddComponent(); bgImg.color = new Color(0.05f, 0.05f, 0.05f, 0.85f); Stretch((RectTransform)bgGO.transform); // Fill (rendered on top; fillAmount drives visible width) var fillGO = new GameObject("Fill"); fillGO.transform.SetParent(canvasGO.transform, false); fillImage = fillGO.AddComponent(); fillImage.color = ColorConstructing; fillImage.type = Image.Type.Filled; fillImage.fillMethod = Image.FillMethod.Horizontal; fillImage.fillOrigin = 0; // left to right fillImage.fillAmount = 0f; Stretch((RectTransform)fillGO.transform); } private static void Stretch(RectTransform rt) { rt.anchorMin = Vector2.zero; rt.anchorMax = Vector2.one; rt.offsetMin = Vector2.zero; rt.offsetMax = Vector2.zero; } private void LateUpdate() { if (source == null) return; var stage = source.CurrentStage; bool show = stage == BuildStage.Constructing || stage == BuildStage.Paused; canvasGO.SetActive(show); if (!show) return; fillImage.color = stage == BuildStage.Paused ? ColorPaused : ColorConstructing; fillImage.fillAmount = source.ComputeProgressNormalized(); var cam = Camera.main; if (cam != null) canvasGO.transform.rotation = cam.transform.rotation; } } }