Unity UGUI 血条实现

本文目标

在 Unity 中使用 UGUI 制作角色血条,实现血条跟随玩家模型的效果。
通过使用动态加载的 Slider,动态计算目标模型的头部位置世界坐标在屏幕空间的对应点位置来实现。

另外一种实现是使用 World Canvas 将血条直接绑定到玩家模型上,对此实现方法本文不作描述。

软件版本

  • Unity 2018.1.5f1 Personal (64bit)
  • Visual Studio 2017

接口/数据

HealthBar 提供锁定对象的功能,传入要跟踪对象的引用即可。对生命值的设定进行了简单的封装,也可以直接获取 Slider 组件设置 value 和 maxValue 等参数。颜色也可以相应调整。

组件/UI

组件结构:

1
2
3
4
5
6
7
Canvas
└─HealthBar(Slider,子组件是系统自带的)
├─Background
├─Fill Area
│ └─Fill
└─Handle Slide Area
└─Handle

在 Hierarchy 中,Create/UI/Slider。将自己编写的 HealthBar 脚本挂载到 HealthBar 物体上。

代码

HealthBar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class HealthBar : MonoBehaviour {

public float HealthPoint
{
get
{
return m_Slider.value;
}
set
{
m_Slider.value = value;
}
}

public float MaxHealthPoint
{
get
{
return m_Slider.maxValue;
}
set
{
m_Slider.maxValue = value;
}
}

void Start()
{
if (Canvas == null) Debug.LogError("Please set a canvas for health bar.");
m_SliderRectTransform = gameObject.GetComponent<Slider>().transform as RectTransform;
//transform.parent = Canvas.transform;
transform.SetParent(Canvas.transform);
// 获取物体高度 https://blog.csdn.net/haobaworenle/article/details/53898221
m_ObjectHeight = FollowTarget.GetComponent<MeshRenderer>().bounds.size.y;
Debug.Log("object height." + m_ObjectHeight);
m_Slider = gameObject.GetComponent<Slider>();
}

void Update()
{
UpdateHealthBarPosition();
}

private void LateUpdate()
{
UpdateHealthBarColor();
}

void UpdateHealthBarPosition()
{
Vector3 worldPosition = new Vector3(FollowTarget.transform.position.x,
FollowTarget.transform.position.y + m_ObjectHeight, FollowTarget.transform.position.z);
// 根据NPC头顶的3D坐标换算成它在2D屏幕中的坐标
Vector2 position = Camera.main.WorldToScreenPoint(worldPosition);
m_SliderRectTransform.position = position;
}

void UpdateHealthBarColor()
{
if (m_Slider.value < m_Slider.maxValue * 0.5f)
{
m_FillImage.color = Color.red;
}
else
{
m_FillImage.color = Color.green;
}
if (m_Slider.value <= 0)
{
Debug.Log(gameObject.name + " is dead");
}
}

public Canvas Canvas;
public GameObject FollowTarget; // 血条跟踪的对象
private RectTransform m_SliderRectTransform;
private Slider m_Slider;

[SerializeField] private Image m_FillImage;
private float m_ObjectHeight;
}

HealthBarLoaderSample

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class HealthBarLoaderSample : MonoBehaviour {
public GameObject FollowTarget;
public Canvas Canvas;
private GameObject m_HealthBarGo;
private HealthBar m_HealthBar;
int m_ChangeAmount;

void Start () {
m_HealthBarGo = Instantiate(Resources.Load<GameObject>("Prefabs/HealthBar"));
m_HealthBar = m_HealthBarGo.GetComponent<HealthBar>();
m_HealthBar.gameObject.SetActive(true);
m_HealthBar.FollowTarget = FollowTarget;
m_HealthBar.Canvas = Canvas;
}

private void Update()
{
if (m_HealthBar.HealthPoint <= 0) {
m_ChangeAmount = 1;
} else if (m_HealthBar.HealthPoint >= m_HealthBar.MaxHealthPoint)
{
m_ChangeAmount = -1;
}
m_HealthBar.HealthPoint += m_ChangeAmount;
}
}

代码逻辑

根据NPC头顶的3D坐标,使用 Camera.main.WorldToScreenPoint 函数将其换算成在2D屏幕中的坐标。

生命值到一定值时,改变 Fill 的颜色。

UpdateHealthBarColor 放在 LateUpdate 中为了保证在 Update 中所有伤害计算完了再去定颜色或者确认玩家死亡。

在上述代码中,HealthBar 动态加载后,放在了 Canvas 下并且设定为 Active,这样才能正常显示血条。

HealthBarLoaderSample 给出一个使用示例,可以看到血条值的更新和目标指定等都是由其他脚本负责的。

示例工程

参考资料