312 lines
11 KiB
C#
312 lines
11 KiB
C#
using UnityEngine;
|
|
using TMPro;
|
|
using UnityEngine.UI;
|
|
using UnityEngine.SceneManagement;
|
|
using System.Collections;
|
|
|
|
public class TowerHealth : MonoBehaviour
|
|
{
|
|
public int maxHealth = 100;
|
|
public GameObject floatingTextPrefab;
|
|
|
|
[Header("HUD")]
|
|
public Image healthFill;
|
|
public TextMeshProUGUI healthText;
|
|
public Color highHealthColor = new Color(0.18f, 0.88f, 0.42f);
|
|
public Color lowHealthColor = new Color(0.95f, 0.22f, 0.18f);
|
|
|
|
private int currentHealth;
|
|
private bool isDead;
|
|
private SpriteRenderer spriteRenderer;
|
|
|
|
void Start()
|
|
{
|
|
currentHealth = maxHealth;
|
|
spriteRenderer = GetComponent<SpriteRenderer>();
|
|
if (spriteRenderer != null)
|
|
{
|
|
spriteRenderer.color = Color.white;
|
|
}
|
|
|
|
EnsureHealthBar();
|
|
UpdateHealthBar();
|
|
}
|
|
|
|
public void TakeDamage(int damage)
|
|
{
|
|
if (isDead)
|
|
return;
|
|
|
|
currentHealth -= damage;
|
|
if (currentHealth < 0) currentHealth = 0;
|
|
ShowFloatingDamage(damage);
|
|
UpdateHealthBar();
|
|
|
|
if (currentHealth <= 0)
|
|
{
|
|
isDead = true;
|
|
ShowStageLostScreen();
|
|
}
|
|
}
|
|
|
|
void UpdateHealthBar()
|
|
{
|
|
float healthPercentage = maxHealth <= 0 ? 0f : (float)currentHealth / maxHealth;
|
|
|
|
if (healthFill != null)
|
|
{
|
|
healthFill.fillAmount = 1f;
|
|
healthFill.color = Color.Lerp(lowHealthColor, highHealthColor, healthPercentage);
|
|
|
|
RectTransform fillRect = healthFill.rectTransform;
|
|
fillRect.anchorMax = new Vector2(healthPercentage, fillRect.anchorMax.y);
|
|
fillRect.offsetMax = Vector2.zero;
|
|
}
|
|
|
|
if (healthText != null)
|
|
{
|
|
healthText.text = $"{currentHealth}/{maxHealth}";
|
|
}
|
|
}
|
|
|
|
void EnsureHealthBar()
|
|
{
|
|
if (healthFill != null && healthText != null)
|
|
return;
|
|
|
|
Canvas canvas = FindAnyObjectByType<Canvas>();
|
|
if (canvas == null)
|
|
{
|
|
GameObject canvasObject = new GameObject("GameUI", typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
|
|
canvas = canvasObject.GetComponent<Canvas>();
|
|
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
|
|
|
CanvasScaler scaler = canvasObject.GetComponent<CanvasScaler>();
|
|
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
|
scaler.referenceResolution = new Vector2(800f, 600f);
|
|
scaler.matchWidthOrHeight = 0.5f;
|
|
}
|
|
|
|
Transform existing = canvas.transform.Find("TowerHealthBar");
|
|
RectTransform container = existing != null
|
|
? existing.GetComponent<RectTransform>()
|
|
: CreateRect("TowerHealthBar", canvas.transform);
|
|
|
|
container.anchorMin = new Vector2(1f, 1f);
|
|
container.anchorMax = new Vector2(1f, 1f);
|
|
container.pivot = new Vector2(1f, 1f);
|
|
container.anchoredPosition = new Vector2(-24f, -24f);
|
|
container.sizeDelta = new Vector2(240f, 54f);
|
|
|
|
Image panel = container.GetComponent<Image>();
|
|
if (panel == null)
|
|
panel = container.gameObject.AddComponent<Image>();
|
|
panel.color = new Color(0.05f, 0.06f, 0.08f, 0.84f);
|
|
|
|
TextMeshProUGUI label = container.Find("Label")?.GetComponent<TextMeshProUGUI>();
|
|
if (label == null)
|
|
{
|
|
label = CreateText("Label", container, "TOWER", 14f);
|
|
RectTransform labelRect = label.rectTransform;
|
|
labelRect.anchorMin = new Vector2(0f, 1f);
|
|
labelRect.anchorMax = new Vector2(0f, 1f);
|
|
labelRect.pivot = new Vector2(0f, 1f);
|
|
labelRect.anchoredPosition = new Vector2(12f, -7f);
|
|
labelRect.sizeDelta = new Vector2(90f, 18f);
|
|
}
|
|
|
|
if (healthText == null)
|
|
{
|
|
healthText = container.Find("Value")?.GetComponent<TextMeshProUGUI>();
|
|
if (healthText == null)
|
|
{
|
|
healthText = CreateText("Value", container, "", 14f);
|
|
healthText.alignment = TextAlignmentOptions.Right;
|
|
RectTransform textRect = healthText.rectTransform;
|
|
textRect.anchorMin = new Vector2(1f, 1f);
|
|
textRect.anchorMax = new Vector2(1f, 1f);
|
|
textRect.pivot = new Vector2(1f, 1f);
|
|
textRect.anchoredPosition = new Vector2(-12f, -7f);
|
|
textRect.sizeDelta = new Vector2(100f, 18f);
|
|
}
|
|
}
|
|
|
|
RectTransform track = container.Find("Track")?.GetComponent<RectTransform>();
|
|
if (track == null)
|
|
{
|
|
track = CreateRect("Track", container);
|
|
track.anchorMin = new Vector2(0f, 0f);
|
|
track.anchorMax = new Vector2(1f, 0f);
|
|
track.pivot = new Vector2(0.5f, 0f);
|
|
track.anchoredPosition = new Vector2(0f, 10f);
|
|
track.sizeDelta = new Vector2(-24f, 16f);
|
|
|
|
Image trackImage = track.gameObject.AddComponent<Image>();
|
|
trackImage.color = new Color(0.16f, 0.17f, 0.2f, 1f);
|
|
}
|
|
|
|
if (healthFill == null)
|
|
{
|
|
healthFill = track.Find("Fill")?.GetComponent<Image>();
|
|
if (healthFill == null)
|
|
{
|
|
RectTransform fill = CreateRect("Fill", track);
|
|
fill.anchorMin = Vector2.zero;
|
|
fill.anchorMax = Vector2.one;
|
|
fill.offsetMin = Vector2.zero;
|
|
fill.offsetMax = Vector2.zero;
|
|
|
|
healthFill = fill.gameObject.AddComponent<Image>();
|
|
healthFill.type = Image.Type.Filled;
|
|
healthFill.fillMethod = Image.FillMethod.Horizontal;
|
|
healthFill.fillOrigin = (int)Image.OriginHorizontal.Left;
|
|
}
|
|
}
|
|
|
|
healthFill.type = Image.Type.Simple;
|
|
}
|
|
|
|
void ShowStageLostScreen()
|
|
{
|
|
Time.timeScale = 0f;
|
|
|
|
Canvas canvas = FindAnyObjectByType<Canvas>();
|
|
if (canvas == null)
|
|
return;
|
|
|
|
GameObject overlay = new GameObject("StageLostOverlay", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
|
|
RectTransform rect = overlay.GetComponent<RectTransform>();
|
|
rect.SetParent(canvas.transform, false);
|
|
rect.anchorMin = Vector2.zero;
|
|
rect.anchorMax = Vector2.one;
|
|
rect.offsetMin = Vector2.zero;
|
|
rect.offsetMax = Vector2.zero;
|
|
rect.SetAsLastSibling();
|
|
|
|
Image background = overlay.GetComponent<Image>();
|
|
background.color = new Color(0f, 0f, 0f, 0.8f);
|
|
background.raycastTarget = true;
|
|
|
|
TextMeshProUGUI title = CreateText("Title", rect, "STAGE LOST", 44f);
|
|
title.alignment = TextAlignmentOptions.Center;
|
|
PlaceOverlayRect(title.rectTransform, 0f, 64f, 420f, 68f);
|
|
|
|
TextMeshProUGUI subtitle = CreateText("Subtitle", rect, "Wave progress and upgrades were lost.", 19f);
|
|
subtitle.fontStyle = FontStyles.Normal;
|
|
subtitle.alignment = TextAlignmentOptions.Center;
|
|
PlaceOverlayRect(subtitle.rectTransform, 0f, 12f, 520f, 42f);
|
|
|
|
Button menuButton = CreateOverlayButton("MenuButton", rect, "MAIN MENU", ReturnToMenu);
|
|
PlaceOverlayRect(menuButton.GetComponent<RectTransform>(), 0f, -64f, 220f, 54f);
|
|
|
|
StartCoroutine(ReturnToMenuAfterDelay());
|
|
}
|
|
|
|
IEnumerator ReturnToMenuAfterDelay()
|
|
{
|
|
yield return new WaitForSecondsRealtime(2.25f);
|
|
ReturnToMenu();
|
|
}
|
|
|
|
void ReturnToMenu()
|
|
{
|
|
Time.timeScale = 1f;
|
|
SceneManager.LoadScene("MainMenu");
|
|
}
|
|
|
|
static RectTransform CreateRect(string name, Transform parent)
|
|
{
|
|
GameObject obj = new GameObject(name, typeof(RectTransform));
|
|
RectTransform rect = obj.GetComponent<RectTransform>();
|
|
rect.SetParent(parent, false);
|
|
return rect;
|
|
}
|
|
|
|
static TextMeshProUGUI CreateText(string name, Transform parent, string text, float fontSize)
|
|
{
|
|
GameObject obj = new GameObject(name, typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI));
|
|
RectTransform rect = obj.GetComponent<RectTransform>();
|
|
rect.SetParent(parent, false);
|
|
|
|
TextMeshProUGUI label = obj.GetComponent<TextMeshProUGUI>();
|
|
label.text = text;
|
|
label.fontSize = fontSize;
|
|
label.fontStyle = FontStyles.Bold;
|
|
label.color = new Color(0.92f, 0.95f, 1f, 1f);
|
|
label.raycastTarget = false;
|
|
return label;
|
|
}
|
|
|
|
static Button CreateOverlayButton(string name, Transform parent, string label, UnityEngine.Events.UnityAction onClick)
|
|
{
|
|
GameObject obj = new GameObject(name, typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button));
|
|
RectTransform rect = obj.GetComponent<RectTransform>();
|
|
rect.SetParent(parent, false);
|
|
|
|
Image image = obj.GetComponent<Image>();
|
|
image.color = new Color(0.18f, 0.5f, 0.74f, 1f);
|
|
|
|
Button button = obj.GetComponent<Button>();
|
|
ColorBlock colors = button.colors;
|
|
colors.normalColor = image.color;
|
|
colors.highlightedColor = new Color(0.25f, 0.62f, 0.88f, 1f);
|
|
colors.pressedColor = new Color(0.12f, 0.36f, 0.56f, 1f);
|
|
colors.selectedColor = colors.highlightedColor;
|
|
colors.disabledColor = new Color(0.16f, 0.18f, 0.22f, 1f);
|
|
colors.colorMultiplier = 1f;
|
|
button.colors = colors;
|
|
button.onClick.AddListener(onClick);
|
|
|
|
TextMeshProUGUI text = CreateText("Text", rect, label, 18f);
|
|
text.alignment = TextAlignmentOptions.Center;
|
|
text.rectTransform.anchorMin = Vector2.zero;
|
|
text.rectTransform.anchorMax = Vector2.one;
|
|
text.rectTransform.offsetMin = Vector2.zero;
|
|
text.rectTransform.offsetMax = Vector2.zero;
|
|
return button;
|
|
}
|
|
|
|
static void PlaceOverlayRect(RectTransform rect, float x, float y, float width, float height)
|
|
{
|
|
rect.anchorMin = new Vector2(0.5f, 0.5f);
|
|
rect.anchorMax = new Vector2(0.5f, 0.5f);
|
|
rect.pivot = new Vector2(0.5f, 0.5f);
|
|
rect.anchoredPosition = new Vector2(x, y);
|
|
rect.sizeDelta = new Vector2(width, height);
|
|
}
|
|
|
|
void ShowFloatingDamage(int damage)
|
|
{
|
|
if (floatingTextPrefab != null)
|
|
{
|
|
GameObject damageTextInstance = Instantiate(
|
|
floatingTextPrefab,
|
|
transform.position + new Vector3(0, 1.5f, 0),
|
|
Quaternion.identity
|
|
);
|
|
TextMeshPro tmp = damageTextInstance.GetComponent<TextMeshPro>();
|
|
tmp.text = "-" + damage.ToString();
|
|
StartCoroutine(FadeAndMoveText(damageTextInstance));
|
|
Destroy(damageTextInstance, 1.5f);
|
|
}
|
|
}
|
|
|
|
IEnumerator FadeAndMoveText(GameObject textObj)
|
|
{
|
|
float duration = 1f;
|
|
float elapsed = 0f;
|
|
Vector3 startPos = textObj.transform.position;
|
|
Vector3 targetPos = startPos + new Vector3(0, 1f, 0); // Move up
|
|
TextMeshPro tmp = textObj.GetComponent<TextMeshPro>();
|
|
Color startColor = tmp.color;
|
|
while (elapsed < duration)
|
|
{
|
|
elapsed += Time.deltaTime;
|
|
float t = elapsed / duration;
|
|
textObj.transform.position = Vector3.Lerp(startPos, targetPos, t);
|
|
tmp.color = new Color(startColor.r, startColor.g, startColor.b, 1 - t);
|
|
yield return null;
|
|
}
|
|
}
|
|
}
|