Unity UGUI学习系列(四) ------ 多层血条实现
程序员文章站
2022-03-26 23:19:47
...
本系列文章是学习siki学院UGUI整体解决方案-案例篇笔记
GitHub地址:https://github.com/BlueMonk1107/UGUISolution
本文实现的是多层血条,效果如下 :
不过这里血条是image,update每帧跟随目标,个人觉得用SpriteRender实现好像更好一点,不过也当学习记录了
一.血条预制体
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<Canvas>();
if (canvas == null)
{
Debug.LogError("场景中没有Canvas组件");
return;
}
SpwanLifeBar(canvas);
}
//实例化血条
private void SpwanLifeBar(Canvas canvas)
{
GameObject prefab = Resources.Load<GameObject>("LifeBar");
_bar = Instantiate(prefab, canvas.transform).AddComponent<LifeBar>();
List<LifeBarData> data = new List<LifeBarData>();
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<LifeBarData> _data;
private LifeBarItem _nextBar;
private LifeBarItem _currentBar;
private float _unitLifeScale;
private int _currentIndex;
public void Init(Transform target, int lifeMax, List<LifeBarData> data)
{
_currentIndex = 0;
_target = target;
_offset = GetOffset(target);
_data = data;
_nextBar = transform.Find("NextBar").gameObject.AddComponent<LifeBarItem>();
_currentBar = transform.Find("CurrentBar").gameObject.AddComponent<LifeBarItem>();
_nextBar.Init();
_currentBar.Init();
RectTransform rect = GetComponent<RectTransform>();
_unitLifeScale = rect.rect.width * data.Count / lifeMax;
SetBarData(_currentIndex,data);
}
private Vector3 GetOffset(Transform target)
{
Renderer renderer = target.GetComponent<Renderer>();
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<LifeBarData> 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<RectTransform>();
return _rect;
}
}
private Image _image;
public Image Image
{
get
{
if (_image == null)
_image = GetComponent<Image>();
return _image;
}
}
private LifeBarItem _child;
private float _defaultWidth;
public void Init()
{
if (transform.Find("AdditionBar") != null)
//添加LifeBarItem脚本保证两者保持一致
_child = transform.Find("AdditionBar").gameObject.AddComponent<LifeBarItem>();
_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;
}
}