一:效果演示

Unity中实现多层血条_ide


二:使用

——面板参数设置
Unity中实现多层血条_c#_02
HpSlider:Slider组件
TopBar:顶部血条
DownBar:底部血条
LastBarColor:最后一条血条的颜色
T:血量改变时缓动的插值(如果不需要缓动效果则设置为1即可)

——常规使用
创建Slider组件,添加HpBar脚本

using System.Collections.Generic;
using UnityEngine;

public class Test : MonoBehaviour
{
    public HpBar hpBar;

    private void Awake()
    {
        List<Color> colorList = new List<Color>() { Color.blue, Color.cyan, Color.green, Color.red };
        hpBar.SetHpBar(100, colorList);
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            hpBar.ChangeValue(6);
        }
        if (Input.GetKeyDown(KeyCode.D))
        {
            hpBar.ChangeValue(-8);
        }
    }
}

——其他使用
CurIndex:当前第几管血
CurHp:当前血量
OnValueChanged:每次血量改变时的回调
OnIndexChanged:每次血条下标改变时的回调


三:代码实现

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;

/// <summary>
/// 血条
/// </summary>
public class HpBar : MonoBehaviour
{
    //血条slider组件
    public Slider hpSlider;
    //顶部血条
    public Image topBar;
    //底部血条
    public Image downBar;

    //最后一个血条颜色
    public Color lastBarColor;
    //缓动差值
    public float t = 0.1f;
    //血条数值变化后
    public Action<int, long> OnValueChanged;
    //血条下标变化后
    public Action<int, long> OnIndexChanged;

    //当前下标(第几管血,降序)
    int m_CurIndex;
    public int CurIndex
    {
        get
        {
            return m_CurIndex;
        }
    }
    //当前血量
    long m_CurHp;
    public long CurHp
    {
        get
        {
            return m_CurHp;
        }
    }
    //总血量
    long m_TotalHp;
    //每一管血条的血量
    float m_PerValue;
    //血条颜色列表
    List<Color> m_ColorList = new List<Color>();
    //目标值
    float m_TargetValue;
    //目标下标
    int m_TargetIndex;

    /// <summary>
    /// 设置血条
    /// </summary>
    public void SetHpBar(long totalHp, List<Color> colorList, long curHp = -1)
    {
        m_ColorList.Clear();
        m_TotalHp = totalHp;
        m_ColorList = colorList;
        m_CurHp = curHp == -1
            ? m_TotalHp
            : curHp;
        InitHpBar();
    }

    /// <summary>
    /// 设置血条
    /// </summary>
    public void SetHpBar(long totalHp, List<string> colorList, long curHp = -1)
    {
        m_ColorList.Clear();
        m_TotalHp = totalHp;
        foreach (var colorHtml in colorList)
        {
            Color color;
            ColorUtility.TryParseHtmlString(colorHtml, out color);
            m_ColorList.Add(color);
        }
        m_CurHp = curHp == -1
           ? m_TotalHp
           : curHp;

        InitHpBar();
    }

    /// <summary>
    /// 初始化血条
    /// </summary>
    void InitHpBar()
    {
        m_CurIndex = m_ColorList.Count;
        m_PerValue = m_TotalHp * 1f / m_ColorList.Count;
        hpSlider.minValue = 0;
        hpSlider.maxValue = m_PerValue;
        hpSlider.value = m_PerValue;

        UpdateIndexAndValue();
        UpdateHpBar(true);
        UpdateColor();
        OnValueChanged?.Invoke(m_CurIndex, m_CurHp);
    }

    /// <summary>
    /// 更新血条颜色
    /// </summary>
    void UpdateColor()
    {
        topBar.color = m_ColorList[m_CurIndex - 1];
        downBar.color = m_CurIndex - 1 <= 0
            ? lastBarColor
            : m_ColorList[m_CurIndex - 2];
    }

    /// <summary>
    /// 更新目标下标和目标值
    /// </summary>
    void UpdateIndexAndValue()
    {
        //更新下标
        int targetIndex = 1;
        int indexOffset = m_CurHp % m_PerValue == 0
            ? Mathf.FloorToInt(m_CurHp / m_PerValue) - 1
            : Mathf.FloorToInt(m_CurHp / m_PerValue);
        targetIndex = Mathf.Clamp(targetIndex + indexOffset, 1, m_ColorList.Count);
        m_TargetIndex = targetIndex;

        //更新值
        float targetValue = 0;
        if (m_TargetIndex >= m_ColorList.Count
            && m_CurHp % m_PerValue == 0)
        {
            targetValue = m_PerValue;
        }
        else
        {
            targetValue = m_CurHp % m_PerValue;
        }
        m_TargetValue = targetValue;

        OnValueChanged?.Invoke(m_CurIndex, m_CurHp);
    }

    /// <summary>
    /// 更新血条
    /// </summary>
    void UpdateHpBar(bool isForce)
    {
        if (isForce)
        {
            m_CurIndex = m_TargetIndex;
            hpSlider.value = m_TargetValue;
        }
        else
        {
            if (m_CurIndex == m_TargetIndex)
            {
                hpSlider.value = Mathf.Lerp(hpSlider.value, m_TargetValue, t);
                if (Mathf.Abs(hpSlider.value - m_TargetValue) <= 0.01f)
                {
                    hpSlider.value = m_TargetValue;
                }
            }
            else
            {
                if (m_CurIndex < m_TargetIndex)
                {
                    hpSlider.value = Mathf.Lerp(hpSlider.value, m_PerValue, t);
                    if (Mathf.Abs(hpSlider.value - m_PerValue) <= 0.01f)
                    {
                        m_CurIndex++;
                        hpSlider.value = 0;
                        OnIndexUpdate();
                    }
                }
                else if (m_CurIndex > m_TargetIndex)
                {
                    hpSlider.value = Mathf.Lerp(hpSlider.value, 0, t);
                    if (Mathf.Abs(hpSlider.value - 0) <= 0.01f)
                    {
                        m_CurIndex--;
                        hpSlider.value = m_PerValue;
                        OnIndexUpdate();
                    }
                }
            }
        }
    }

    /// <summary>
    /// 改变血条数值
    /// </summary>
    public void ChangeValue(int value)
    {
        m_CurHp = (long)Mathf.Clamp(m_CurHp + value, 0, m_TotalHp);

        UpdateIndexAndValue();
    }

    private void Update()
    {
        UpdateHpBar(false);
    }

    /// <summary>
    /// 血条下标变化后
    /// </summary>
    void OnIndexUpdate()
    {
        UpdateColor();

        OnIndexChanged?.Invoke(m_CurIndex, m_CurHp);
    }
}