NGUI实现多层血条[交流贴]

写在前面(废话)

想用NGUI实现个多层血条,在网上没找到相关信息,思来想去还是自己实现一个吧。最后捣鼓捣鼓还是出来了。写了两个版本的,第一个版本是纯体力制作,就是有几层血就预先放好几个血条,比如说 boss有5层血,就预先摆好5层血,但是要加个透明血条(用来表现减血效果,如下图示例),就需要摆10个血条了,这样就太挫了,于是就有了多层血条2.0 beta,之所以用beta,因为觉得还是不够好。开个贴,大家交流一下,希望多提意见。。。微笑


开篇:

一.简介:

1.简单实现原理。预先放置几个血条,当最上面一层血减完为0(缩小为0)后,把它移动到底层,然后再放到的正常大小,并且改变图片显示或者颜色为指定显示。

2. 本版多层血条,用了3个血条对象,每个血条对象包括一个全色显示的血条和一个半透明的血条,实际上用到了6个sprite,如下图示。先前想用2个血条对象(2个要简单大笑),后来考虑到会有一种情况,就是当本次伤害减血的值大于当前血条所剩余值时,下面的血条也会瞬间减血,因为只有两条血,在最上面这层血减完,还未移动到底层的时候,这样的话显示就会露馅。不过实际上我没有用2层对象来做过测试,具体会不会出现这种情况,也只是处于理论阶段......  : - )

3.这个演示版本的血条从底向上摆放的血条颜色依次为红,绿,蓝。sprite深度depth,依次增加。计数也是从底向上计数,依次为0, 1, 2。如下面层面图演示。





二.代码

1.血条对象。

上面讲到,是用了三个血条对象实现的,每个血条对象包括两个血条sprite,一个是全色的,一个是半透明的,为此就单独写个脚本挂到血条对象上,来做管理。代码如下。

简单说明:

1).血条是用sprite制作的

2).不同层的血条会使用不同颜色的图片.示例使用了三种颜色的图片依次为红,绿,蓝。

3).显示的顺序是从底向上的,即索引值从底向上依次为0,1,2

主要功能:

1),使用TweenScale进行缩小

2).改变depth并切换sprite图片

3).显示或者隐藏

P.S.请不要直接拷贝代码使用,出错了不管哦大笑


using UnityEngine;
using System.Collections;

public class MuHp : MonoBehaviour
{
    GameObject _normalHp;
    GameObject _transHp;

    UISprite _normalSp;
    UISprite _transSp;

    TweenScale _normalScale;
    TweenScale _transScale;

    /// <summary>
    /// Tween Speed
    /// </summary>
    float tweenSpeed = 1 / 0.7f;

    private int _depth = 0;
    private int _picIndex = 0;

    /// <summary>
    /// 血条对应的图片 依次为蓝色,绿色,红色
    /// </summary>
    string[] _hpPic = { "Hp03", "Hp02", "Hp01" };

    void OnEnable()
    {
        //1 - 查找对象
        _normalHp = transform.Find("normal").gameObject;
        _transHp = transform.Find("trans").gameObject;

        //2 - 获取normal血条脚本 sprite和TweenScale
        _normalSp = _normalHp.GetComponent<UISprite>();
        _normalScale = _normalHp.GetComponent<TweenScale>();
        //2.1 - 给TweenScale绑定动画结束回调
        _normalScale.AddOnFinished(PlayScaleFinished);

        //3 - 获取transparent血条脚本 sprite 和 TweenScale
        _transSp = _transHp.GetComponent<UISprite>();
        _transScale = _transHp.GetComponent<TweenScale>();
    }

    /// <summary>
    /// 血条显示控制
    /// </summary>
    /// <param name="bIs"></param>
    public void SetVisible(bool bIs)
    {
        _normalHp.SetActive(bIs);
        _transHp.SetActive(bIs);
    }

    /// <summary>
    /// 设置血条图片并放大到正常大小
    /// </summary>
    public void SetShowPic()
    {
        _normalSp.spriteName = _hpPic[_picIndex];
        _transSp.spriteName = _hpPic[_picIndex];

        SetScale(Vector3.one);
    }

    /// <summary>
    /// 设置血条depth
    /// </summary>
    /// <param name="dep"></param>
    public void SetHpDepth(int dep)
    {

        _normalSp.depth = dep;
        _transSp.depth = dep - 1;
    }

    /// <summary>
    /// 改变血条depth
    /// </summary>
    public int HpDepth
    {
        set
        {
            _depth = value;
            _normalSp.depth = _depth;
            _transSp.depth = _depth - 1;
        }

        get { return _depth; }
    }

    /// <summary>
    /// 血条图片索引
    /// </summary>
    public int HpPicIndex
    {
        set { _picIndex = value; }

        get { return _picIndex; }
    }

    /// <summary>
    /// 设置血条缩放
    /// </summary>
    public void SetScale(Vector3 vScale)
    {
        _normalScale.value = vScale;
        _transScale.value = vScale;
    }

    /// <summary>
    /// 获取正常血条对象
    /// </summary>
    public GameObject NormalHp
    {
        set
        {
            _normalHp = value;
        }

        get { return _normalHp; }
    }

    /// <summary>
    /// 获取透明血条对象
    /// </summary>
    public GameObject TransHp
    {
        set
        {
            _transHp = value;
        }
        get { return _transHp; }
    }

    /// <summary>
    /// 播放动画
    /// </summary>
    public void PlayTween(float to)
    {
        _normalScale.from = _normalScale.value;
        _normalScale.to.x = to;
        _normalScale.duration = (_normalScale.from.x - _normalScale.to.x) / tweenSpeed;
        _normalScale.PlayForward();
    }


    /// <summary>
    /// normal缩放结束,trans开始动画
    /// </summary>
    void PlayScaleFinished()
    {
        _transAlpha.from = _initAlpha;
        _transAlpha.to = 0;
        _transAlpha.delay = 0.4f;
        _transAlpha.PlayForward();

    }

    /// <summary>
    /// 淡出结束 缩放到前景一样的长度 并恢复Alpha值
    /// </summary>
    void PlayAlphaFinished()
    {
        _transScale.value = _normalScale.value;
        _transAlpha.value = _initAlpha;
    }

}

2.血条操作对象Controller

说明: 如上所说,本例使用3个血条对象实现多层血条效果。首先根据初始血条层数和最大血量,来初始化血条的显示,然后根据每次剩余血量和总血量来计算相关数据来做显示处理。为了方便说明,会把血条对象的depth说为12,14,16,其实是指代对象里实体血条sprite的depth。

要点:

2.1UI制作。预先在界面中放置好3个血条对象,每个对象里的sprite要有不同的depth。应该从底到高显示。对象里有两个sprite,一个为正常显示的,一个为半透明的,半透明的depth比正常的低一个值。本例为第一个对象中半透明血条depth为11,正常血条depth为12,第二个对象半透明血条depth为13,正常血条depth为14,第二个对象半透明血条depth为15,正常血条depth为16。

2.2.几个参数变量

1).血条对象脚本MuHp中的血条图片名字数组  -  _hpPic.这个数组放的是血条所用图片的名字,图片颜色依次为蓝,绿,红,对应初始的血条摆放显示顺序,即从底向上依次为红,绿,蓝。在HpController脚本里,当一个血条消耗完毕,移动到底部时,会根据计算的index来从_hpPic里取图片来做显示改变。

2).HpController中名为_muHp的List。这个list中存放的是事先摆放好的三个血条对象,从index 0开始计数,依次为 0, 1, 2,分别对应的血条深度依次为12, 14, 16。即从0开始计数,深度依次增加。

3).HpController中名为_listQueue的Queue。这个Queue存放的是int 值,初始值为 0, 1, 2分别对应于_muHp中的index,当最高index = 2的血条消耗完下降到底部时,这个队列会Dequeue最上面的值,然后Enqueue到自身,依此来一一对应记录_muHp中血条目前真实的index,可以得到当前Top血层在_muHp中的index。

4).其他参数如记录最大血层数_ulayerNum,记录当前血层数的值_layerCount,在血条初始化显示和血条变化过程中会用到。


2.3.主要方法

1).是初始化显示方法。CalculateInitValue用来做初始化数值和图片显示,关键是大于3层血的显示,本例显示规则是从底向上依次为红绿蓝红绿蓝........这样循环显示。

2).根据数值计数显示。CalculateHp。这个方法根据传来的剩余血量和总血量,来计算当前血层是第几层血(从0开始计数),以及当前血层的血量百分比。用两个计算就可以搞定了。还有一个就是当前血层消耗完的判断。具体看代码。

3).循环遍历显示调整深度和图片显示。AdjustDepth。这个方法会在有一层血消耗完并且剩余血层数量大于等3的时候运行。用来改变每个血条对象的深度,并且改变下降到底层血条的显示。

P.S.请不要直接拷贝代码使用,出错了不管哦大笑

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


public class HpController : MonoBehaviour
{
    /// <summary>
    /// 使用三个血条来实现多层血条效果
    /// List中的血条对象index依此为0, 1, 2.深度依此为 12, 14, 16   !!!! - 重要
    /// </summary>
    List<MuHp> _muHp = new List<MuHp>();

    float fTest = 1.0f;

    /// <summary>
    /// 血条List中index,调整其depth后对应的index
    /// 初始添加为 2, 1, 0,对应_muHp里血条对象的 index     !!!! - 重要
    /// </summary>
    Queue<int> _listQueue = new Queue<int>();

    /// <summary>
    /// 总层数
    /// </summary>
    int _ulayerNum = 3;

    /// <summary>
    /// 当前还有几层血
    /// </summary>
    int _layerCount = 0;

    /// <summary>
    /// 每层的血量
    /// </summary>
    float _layerHpValue = 0;

    /// <summary>
    /// 上一层血是属于那一层 - 总血层减去1
    /// </summary>
    int _lastLayer = 0;

    //血条颜色排列 从底向上      !!!! - 重要

    //蓝色 - 2
    //绿色 - 1
    //红色 - 0

    void Awake()
    {
        FindUI();
    }

    //void Start() 
    //{
    //    CalculateInitValue(5, 1200);
    //}

    //float maxHp = 1200;
    //float curHp = 1200;
    //void Update()
    //{
    //    if (Input.GetKeyDown(KeyCode.W))
    //    {
    //        int test = Random.Range(20, 60);
    //        curHp -= test;
    //        CalculateHp(curHp, maxHp);
    //    }
    //}

    /// <summary>
    /// 查找血条对象并绑定MuHp脚本
    /// </summary>
    void FindUI()
    {
        for (int i = 0; i < 3; i++)
        {
            string strHp = string.Format("Hp{0}", i);

            //找到3个血条对象
            GameObject hp = transform.Find(strHp).gameObject;
            //添加MuHp.cs脚本
            MuHp hpSc = hp.AddComponent<MuHp>();
            //从depth低到高,依此添加到List中     !!!! - 重要
            _muHp.Add(hpSc);
        }

    }

    #region 血条
    /// <summary>
    /// 根据配置计算相关初始化数据
    /// 说明:每层血的血量相同   !!!! - 重要
    /// </summary>
    /// <param name="layer">血条的总层数</param>
    /// <param name="maxHp">最大血量</param>
    public void CalculateInitValue(int layer, float maxHp)
    {
        _listQueue.Clear();

        //血条总数
        _ulayerNum = layer;
        //总数计数
        _layerCount = layer;
        //每层血的血量 
        _layerHpValue = maxHp / layer;

        //用来判断某一层的血条是否减完了,初始值为最大层数
        //层数计数和数组计数一样,最小从0开始记
        _lastLayer = layer - 1;

        //处理血条初始图片显示 
        HandleInitShow(_ulayerNum);

        //初始index 队列
        _listQueue.Enqueue(2);
        _listQueue.Enqueue(1);
        _listQueue.Enqueue(0);
    }

    /// <summary>
    /// 处理血条初始化显示
    /// 主要做初始化图片显示,或者隐藏多余的
    /// 要点:
    /// 1.MuHp.cs 中的 _hpPic 图片名字数组
    /// 2.总数大于三条的显示。举例说明,比如现在总血层为4条
    /// 则当前3个血条从底向上的显示应该依次为 绿 蓝 红,二不是红 绿 蓝了,本例依此规则显示。     !!!!! - 重要
    /// </summary>
    /// <param name="totalLayer"></param>
    void HandleInitShow(int totalLayer)
    {
        _muHp[0].HpDepth = 12;
        _muHp[1].HpDepth = 14;
        _muHp[2].HpDepth = 16;

        if (totalLayer < 3)//总数不足3条的,隐藏多余的
        {
            for (int i = 2; i > totalLayer - 1; i--)
            {
                _muHp[i].SetVisible(false);
            }
        }
        else if (totalLayer > 3)//总数大于3条的
        {
            //从低到高 0 1 2 0 1 2 
            for (int i = 0; i < 3; i++)
            {
                int picIn = (totalLayer - 3 + i) % 3;

                _muHp[i].HpPicIndex = picIn;
                _muHp[i].SetVisible(true);
                _muHp[i].SetShowPic();
            }
        }
        else//总数正好3条
        {
            for (int i = 0; i < 3; i++)
            {
                _muHp[i].HpPicIndex = i;
                _muHp[i].SetVisible(true);
                _muHp[i].SetShowPic();
            }
            Debug.Log("The Three Hp is Good");
        }
    }

    /// <summary>
    /// 血条更新
    /// </summary>
    /// <param name="hp">当前血量</param>
    /// <param name="maxhp">最大血量</param>
    void UpdateBossHp(float hp, float maxhp)
    {
        float hpRate = Mathf.Clamp(hp / maxhp, 0, 1);
        Debug.Log("hp = " + hp);
        Debug.Log("maxHP = " + maxhp);
        CalculateHp(hp, maxhp);
    }

    /// <summary>
    /// 计算数值改变显示
    /// </summary>
    /// <param name="hp">当前剩余血量</param>
    /// <param name="maxhp">满血血量</param>
    void CalculateHp(float hp, float maxhp)
    {
        //hp 为 0,Boss挂掉了
        if (hp <= 0)
        {
            hp = 0;

            _layerCount = 0;
        }

        if (_layerCount <= 0)
        {
            _layerNum.text = "X0";

            foreach (var hpSc in _muHp)
            {
                hpSc.SetVisible(false);
            }
        }
        else
        {
            _layerNum.text = "X" + _layerCount.ToString();
        }


        //1 - 计算当前血条时第几层血 计数从0开始
        //真实层数 计数减一 比如有三层血,则最高层计数为 2 
        //- 剩余除以每层的平均血量取得当前所处的层数
        int curLayer = Mathf.FloorToInt(hp / _layerHpValue);

        //2 - 计算当前血条剩余比值
        //求余数算法
        //1).求余之后就是当前层所剩余的血
        //2).然后再和平均血量做比就是比值了
        float layerRatio = hp % _layerHpValue / _layerHpValue;

        //取得当前血条在三个真实血条中的索引值
        int curIndex = _listQueue.Peek();

        //缩放动画
        _muHp[curIndex].PlayTween(layerRatio);

        //通过与last的值比较,来判断这层血是不是减完了
        if (curLayer != _lastLayer)
        {
            if (_layerCount == 0)
            {
                return;
            }

            //计数减一
            _layerCount -= 1;

            //取出这个index
            int downIndex = _listQueue.Dequeue();

            //本层血结束了,如果剩余血层的数量在3之上,则重新排下深度
            if (_ulayerNum > 3 && _layerCount >= 3)
            {
                AdjustDepth(downIndex);

                //并把耗完的这层血的index放到队列里
                _listQueue.Enqueue(downIndex);
            }
            else//不足3层血了,直接缩小为0即可
            {
                _muHp[curIndex].PlayTween(0);
            }

            //记录下当前层
            _lastLayer = curLayer;
        }
    }


    /// <summary>
    /// 调整深度
    /// 重点说明:每消耗完一层血,则运行此方法。方法的主要功能就是遍历三个血条对象
    /// 1.每个血条对象深度加2,如果有血条深度大于最高depth 16,则把它下降到最低depth 12
    /// 并放大的正常大小
    /// 2.降到底部血条的sprite图片在图片数组中的index减3,如果index小于0,则再加3.即图片显示循环了一道.
    /// 然后再改变血条sprite的图片
    /// </summary>
    void AdjustDepth(int downIndex)
    {
        for (int i = 0; i < _muHp.Count; i++)
        {
            _muHp[i].HpDepth += 2;
            if (_muHp[i].HpDepth > 16)
            {
                _muHp[i].HpDepth = 12;
                //放大到正常长度
                _muHp[i].SetScale(Vector3.one);


                _muHp[i].HpPicIndex -= 3;

                if (_muHp[i].HpPicIndex < 0)
                {
                    _muHp[i].HpPicIndex += 3;
                }

                _muHp[i].SetShowPic();
            }
        }
    }

    #endregion
}

结束:

以上为多层血条的具体实现,细看一下还是有很多要点要注意,而且自我感觉写的还是有点小复杂。在此贴个帖子,希望大家多提提意见,我做做改进。

希望不要直接复制上面的两个脚本使用,因为绝对会报错。贴两个脚本主要是方便讲解,让大家看看实现过程,原理大致就是这样。有意见,有改进的地方,多提提哈。谢谢了。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值