Unity UGUI学习系列(四) ------ 多层血条实现

本系列文章是学习siki学院UGUI整体解决方案-案例篇笔记
GitHub地址:https://github.com/BlueMonk1107/UGUISolution

本文实现的是多层血条,效果如下 :

Unity UGUI学习系列(四) ------ 多层血条实现_第1张图片

不过这里血条是image,update每帧跟随目标,个人觉得用SpriteRender实现好像更好一点,不过也当学习记录了

一.血条预制体

Unity UGUI学习系列(四) ------ 多层血条实现_第2张图片
结构

LifeBar上添加LifeBar脚本,CurrentBar / NextBar / AdditionBar 上添加LifeBarItem脚本, AdditionBar出现在这里的目的是为了加减血量渐隐过渡动画

二.脚本解析

Controller脚本 : 实例化LifeBar,按键响应

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

public class Controller : MonoBehaviour
{
    private LifeBar _bar;
    // Start is called before the first frame update
    void Start()
    {
        Canvas canvas = FindObjectOfType();

        if (canvas == null)
        {
            Debug.LogError("场景中没有Canvas组件");
            return;
        }
        SpwanLifeBar(canvas);
    }
    //实例化血条
    private void SpwanLifeBar(Canvas canvas)
    {
        GameObject prefab = Resources.Load("LifeBar");
        _bar = Instantiate(prefab, canvas.transform).AddComponent();
        List data = new List();
        data.Add(new LifeBarData(null,Color.green));
        data.Add(new LifeBarData(null, Color.red));
        data.Add(new LifeBarData(null, Color.yellow));
        _bar.Init(transform,350, data);
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.A))
        {
            Move(Vector3.right);
        }

        if (Input.GetKey(KeyCode.D))
        {
            Move(Vector3.left);
        }

        if (Input.GetKey(KeyCode.S))
        {
            Move(Vector3.down);
        }

        if (Input.GetKey(KeyCode.W))
        {
            Move(Vector3.up);
        }

        if (Input.GetMouseButtonDown(0))
        {
            _bar.ChangeLife(-50);
        }

        if (Input.GetMouseButtonDown(1))
        {
            _bar.ChangeLife(50);
        }
    }

    private void Move(Vector3 direction)
    {
        transform.Translate(direction * Time.deltaTime * 5);
    }
}

LifeBar脚本 : 跟随目标物体,根据索引与data控制LifeBarItem加减血量以及变化

using System.Collections.Generic;
using DG.Tweening;
using UnityEngine;

public class LifeBar : MonoBehaviour
{
    private Transform _target;
    private Vector3 _offset;
    private List _data;
    private LifeBarItem _nextBar;
    private LifeBarItem _currentBar;
    private float _unitLifeScale;
    private int _currentIndex;

    public void Init(Transform target, int lifeMax, List data)
    {
        _currentIndex = 0;
        _target = target;
        _offset = GetOffset(target);
        _data = data;
        _nextBar = transform.Find("NextBar").gameObject.AddComponent();
        _currentBar = transform.Find("CurrentBar").gameObject.AddComponent();
        _nextBar.Init();
        _currentBar.Init();

        RectTransform rect = GetComponent();
        _unitLifeScale = rect.rect.width * data.Count / lifeMax;

        SetBarData(_currentIndex,data);
    }

    private Vector3 GetOffset(Transform target)
    {
        Renderer renderer = target.GetComponent();
        if (renderer == null)
            return Vector3.zero;
        return Vector3.up * renderer.bounds.max.y;
    }

    public void Update()
    {
        if (_target == null)
            return;
        //跟随目标物体
        transform.position = Camera.main.WorldToScreenPoint(_target.position + _offset);
    }
    //加减血量
    public void ChangeLife(float value)
    {
        //得到当前加减血量后image还需要偏移的量
        float width = _currentBar.ChangeLife(value * _unitLifeScale);
        //减少血量
        if (width < 0 && ChangeIndex(1))
        {
            //交换
            Exchange();
            //使_currentBar成为最后一个子物体以达到遮挡的目的
            _currentBar.transform.SetAsLastSibling();
            //设置_nextBar宽度为满值
            _nextBar.ResetToWidth();
            //设置_currentBar与_nextBar的data,以修改颜色与sprite
            SetBarData(_currentIndex,_data);
            //继续减少剩下的width
            ChangeLife(width / _unitLifeScale);
        }
        else if (width > 0 && ChangeIndex(-1))
        {
            Exchange();
            _currentBar.transform.SetAsLastSibling();
            _currentBar.ResetToZero();
            SetBarData(_currentIndex, _data);
            ChangeLife(width/ _unitLifeScale);
        }
    }

    // -1 代表加血 1代表减血
    private bool ChangeIndex(int symbol)
    {
        int index = _currentIndex + symbol;
        if(_data == null)
            return false;
        if (index >= 0 && index < _data.Count)
        {
            _currentIndex = index;
            return true;
        }

        return false;
    }

    private void Exchange()
    {
        var temp = _nextBar;
        _nextBar = _currentBar;
        _currentBar = temp;
    }
    //设置索引index对应的血条
    private void SetBarData(int index, List data)
    {
        if (index < 0 || index >= data.Count)
            return;

        _currentBar.SetData(data[index]);

        if (index + 1 >= data.Count)
        {
            _nextBar.SetData(new LifeBarData(null,Color.white));
        }
        else
        {
            _nextBar.SetData(data[index + 1]);
        }
    }
}
//血条数据,包含Sprite和颜色
public struct LifeBarData
{
    public Sprite BarSprite;
    public Color BarMainColor;

    public LifeBarData(Sprite sprite, Color mainColor)
    {
        BarSprite = sprite;
        BarMainColor = mainColor;
    }
}

LifeBarItem脚本 : 根据索引和data控制image具体宽度,以及加减血量的动画

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

public class LifeBarItem : MonoBehaviour
{
    private RectTransform _rect;
    public RectTransform Rect
    {
        get
        {
            if (_rect == null)
                _rect = GetComponent();
            return _rect;
        }
    }
    private Image _image;

    public Image Image
    {
        get
        {
            if (_image == null)
                _image = GetComponent();
            return _image;
        }
    }
    private LifeBarItem _child;

    private float _defaultWidth;

    public void Init()
    {
        if (transform.Find("AdditionBar") != null)
        //添加LifeBarItem脚本保证两者保持一致
            _child = transform.Find("AdditionBar").gameObject.AddComponent();

        _defaultWidth = Rect.rect.width;
    }

    public void SetData(LifeBarData data)
    {
        Image.color = data.BarMainColor;
        if (data.BarSprite != null)
            Image.sprite = data.BarSprite;

        if(_child != null)
            _child.SetData(data);
    }
    //当前血条加减血量值value
    public float ChangeLife(float value)
    {
        //用子物体做渐隐动画
        if (_child != null)
        {
            _child.DOKill();
            _child.Image.color = Image.color;
            _child.Rect.sizeDelta = Rect.sizeDelta;
            _child.Image.DOFade(0, 0.5f).OnComplete(()=>_child.ChangeLife(value));
        }
        //设置Rect的size
        Rect.sizeDelta += Vector2.right*value;

        return GetOutOfRange();
    }
    //返回加减血量后的偏移量
    private float GetOutOfRange()
    {
        float offset = 0;

        if (Rect.rect.width < 0)
        {
            offset = Rect.rect.width;
            ResetToZero();
        }
        else if(Rect.rect.width > _defaultWidth)
        {
            offset = Rect.rect.width - _defaultWidth;
            ResetToWidth();
        }

        return offset;
    }

    public void ResetToZero()
    {
        Rect.sizeDelta = Vector2.zero;
    }

    public void ResetToWidth()
    {
        Rect.sizeDelta = Vector2.right*_defaultWidth;
    }
}

你可能感兴趣的:(Unity UGUI学习系列(四) ------ 多层血条实现)