193 lines
7.3 KiB
C#
193 lines
7.3 KiB
C#
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
|
||
/// <summary>
|
||
/// Шкала здоровья Босса вверху посередине экрана.
|
||
/// Показывает имя босса, текущее/максимальное здоровье и плавное заполнение полосы.
|
||
/// </summary>
|
||
public class BossHealthBarUI : MonoBehaviour
|
||
{
|
||
private static BossHealthBarUI _instance;
|
||
public static BossHealthBarUI Instance
|
||
{
|
||
get
|
||
{
|
||
if (_instance == null)
|
||
_instance = FindAnyObjectByType<BossHealthBarUI>(FindObjectsInactive.Include);
|
||
return _instance;
|
||
}
|
||
}
|
||
|
||
[Header("Элементы интерфейса")]
|
||
public GameObject rootPanel;
|
||
public Text bossTitleText;
|
||
public Text bossSubText;
|
||
public Image fillBar;
|
||
public Text healthValueText;
|
||
public CanvasGroup canvasGroup;
|
||
|
||
private float targetFill = 1f;
|
||
private float currentFill = 1f;
|
||
private bool isVisible = false;
|
||
|
||
public static BossHealthBarUI Create(Transform hudCanvas)
|
||
{
|
||
var old = hudCanvas.Find("BossHealthBarPanel");
|
||
if (old != null) DestroyImmediate(old.gameObject);
|
||
|
||
Font defaultFont = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
|
||
|
||
// 1. Корневой объект
|
||
GameObject root = new GameObject("BossHealthBarPanel", typeof(RectTransform), typeof(CanvasGroup));
|
||
root.transform.SetParent(hudCanvas, false);
|
||
|
||
RectTransform rootRt = root.GetComponent<RectTransform>();
|
||
rootRt.anchorMin = new Vector2(0.5f, 1.0f);
|
||
rootRt.anchorMax = new Vector2(0.5f, 1.0f);
|
||
rootRt.pivot = new Vector2(0.5f, 1.0f);
|
||
rootRt.anchoredPosition = new Vector2(0f, -22f);
|
||
rootRt.sizeDelta = new Vector2(560f, 62f);
|
||
|
||
// 2. Фон контейнера
|
||
Image bgImg = root.AddComponent<Image>();
|
||
bgImg.color = new Color(0.04f, 0.04f, 0.05f, 0.92f);
|
||
|
||
Outline outline = root.AddComponent<Outline>();
|
||
outline.effectColor = new Color(1.0f, 0.25f, 0.25f, 0.65f);
|
||
outline.effectDistance = new Vector2(1.5f, -1.5f);
|
||
|
||
// 3. Заголовок босса
|
||
GameObject titleObj = new GameObject("BossTitle", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text));
|
||
titleObj.transform.SetParent(root.transform, false);
|
||
RectTransform titleRt = titleObj.GetComponent<RectTransform>();
|
||
titleRt.anchorMin = new Vector2(0f, 0.62f);
|
||
titleRt.anchorMax = new Vector2(1f, 1.0f);
|
||
titleRt.offsetMin = new Vector2(15f, 0f);
|
||
titleRt.offsetMax = new Vector2(-15f, -4f);
|
||
|
||
Text titleText = titleObj.GetComponent<Text>();
|
||
titleText.font = defaultFont;
|
||
titleText.fontSize = 15;
|
||
titleText.fontStyle = FontStyle.Bold;
|
||
titleText.alignment = TextAnchor.MiddleCenter;
|
||
titleText.color = new Color(1.0f, 0.3f, 0.3f, 1.0f);
|
||
titleText.text = "ТИП-0: АЛЬФА-МУТАНТ (ПАТРИАРХ)";
|
||
|
||
// 4. Фон полосы здоровья
|
||
GameObject barBgObj = new GameObject("BarBackground", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
|
||
barBgObj.transform.SetParent(root.transform, false);
|
||
RectTransform barBgRt = barBgObj.GetComponent<RectTransform>();
|
||
barBgRt.anchorMin = new Vector2(0f, 0.16f);
|
||
barBgRt.anchorMax = new Vector2(1f, 0.54f);
|
||
barBgRt.offsetMin = new Vector2(14f, 0f);
|
||
barBgRt.offsetMax = new Vector2(-14f, 0f);
|
||
|
||
Image barBgImg = barBgObj.GetComponent<Image>();
|
||
barBgImg.color = new Color(0.18f, 0.05f, 0.05f, 0.95f);
|
||
|
||
// 5. Заполнение полосы (красный градиент)
|
||
GameObject fillObj = new GameObject("BarFill", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
|
||
fillObj.transform.SetParent(barBgObj.transform, false);
|
||
RectTransform fillRt = fillObj.GetComponent<RectTransform>();
|
||
fillRt.anchorMin = Vector2.zero;
|
||
fillRt.anchorMax = Vector2.one;
|
||
fillRt.sizeDelta = Vector2.zero;
|
||
|
||
Image fillImg = fillObj.GetComponent<Image>();
|
||
fillImg.type = Image.Type.Filled;
|
||
fillImg.fillMethod = Image.FillMethod.Horizontal;
|
||
fillImg.fillOrigin = (int)Image.OriginHorizontal.Left;
|
||
fillImg.color = new Color(0.95f, 0.2f, 0.2f, 1.0f);
|
||
|
||
// 6. Текст числового значения HP
|
||
GameObject hpTxtObj = new GameObject("HealthText", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text));
|
||
hpTxtObj.transform.SetParent(barBgObj.transform, false);
|
||
RectTransform hpTxtRt = hpTxtObj.GetComponent<RectTransform>();
|
||
hpTxtRt.anchorMin = Vector2.zero;
|
||
hpTxtRt.anchorMax = Vector2.one;
|
||
hpTxtRt.sizeDelta = Vector2.zero;
|
||
|
||
Text hpTxt = hpTxtObj.GetComponent<Text>();
|
||
hpTxt.font = defaultFont;
|
||
hpTxt.fontSize = 13;
|
||
hpTxt.fontStyle = FontStyle.Bold;
|
||
hpTxt.alignment = TextAnchor.MiddleCenter;
|
||
hpTxt.color = Color.white;
|
||
hpTxt.text = "500 / 500";
|
||
|
||
BossHealthBarUI barUI = root.AddComponent<BossHealthBarUI>();
|
||
barUI.rootPanel = root;
|
||
barUI.bossTitleText = titleText;
|
||
barUI.fillBar = fillImg;
|
||
barUI.healthValueText = hpTxt;
|
||
barUI.canvasGroup = root.GetComponent<CanvasGroup>();
|
||
barUI.canvasGroup.alpha = 0f;
|
||
barUI.canvasGroup.blocksRaycasts = false;
|
||
barUI.canvasGroup.interactable = false;
|
||
|
||
root.SetActive(false);
|
||
return barUI;
|
||
}
|
||
|
||
private void Awake()
|
||
{
|
||
_instance = this;
|
||
if (canvasGroup == null) canvasGroup = GetComponent<CanvasGroup>();
|
||
}
|
||
|
||
private void Update()
|
||
{
|
||
// Плавное заполнение полосы
|
||
currentFill = Mathf.MoveTowards(currentFill, targetFill, 2.5f * Time.deltaTime);
|
||
if (fillBar != null)
|
||
{
|
||
fillBar.fillAmount = currentFill;
|
||
}
|
||
|
||
// Плавный альфа-фейд
|
||
if (canvasGroup != null)
|
||
{
|
||
float targetAlpha = isVisible ? 1f : 0f;
|
||
canvasGroup.alpha = Mathf.MoveTowards(canvasGroup.alpha, targetAlpha, 4f * Time.deltaTime);
|
||
|
||
if (!isVisible && canvasGroup.alpha <= 0.01f && gameObject.activeSelf)
|
||
{
|
||
gameObject.SetActive(false);
|
||
}
|
||
}
|
||
}
|
||
|
||
public void ShowBoss(string name, float current, float max)
|
||
{
|
||
if (bossTitleText != null) bossTitleText.text = name;
|
||
targetFill = Mathf.Clamp01(current / max);
|
||
currentFill = targetFill;
|
||
if (fillBar != null) fillBar.fillAmount = currentFill;
|
||
if (healthValueText != null) healthValueText.text = $"{Mathf.CeilToInt(current)} / {Mathf.CeilToInt(max)}";
|
||
|
||
gameObject.SetActive(true);
|
||
isVisible = true;
|
||
}
|
||
|
||
public void UpdateHealth(float current, float max)
|
||
{
|
||
targetFill = Mathf.Clamp01(current / max);
|
||
if (healthValueText != null) healthValueText.text = $"{Mathf.Max(0, Mathf.CeilToInt(current))} / {Mathf.CeilToInt(max)}";
|
||
|
||
if (current <= 0f)
|
||
{
|
||
HideBoss(false);
|
||
}
|
||
}
|
||
|
||
public void HideBoss(bool instant = false)
|
||
{
|
||
isVisible = false;
|
||
if (instant)
|
||
{
|
||
if (canvasGroup != null) canvasGroup.alpha = 0f;
|
||
gameObject.SetActive(false);
|
||
}
|
||
}
|
||
}
|