在战斗系统中,血条显示必不可少,而伤害的浮动数值表现也与血条相辅相成。这个DEMO主要实现了简单的战斗中血条表现与伤害数值浮动显示
在Unity3D中可以用UGUI中的Slider简单实现一个血条UI:
有了血条UI后,接下来的工作就是将血条与角色结核。这里可以直接在角色的Class中添加一个UI的Slider变量然后绑定到一个Slider控件上,也可以封装一层Blood类当做控制器用于管理血条,解耦合,避免角色对于UI控件的直接操作。不管怎么实现,最重要的都是:
onIsHited的代码主要如下:
//Blood.onHited(float damage)
public void onIsHited(float damage)
{
bloodValue -= damage;
if (bloodValue < BLOOD_VALUE_MIN)
{
bloodValue = BLOOD_VALUE_MIN;
}
bloodDisplay.BloodValueChange(bloodValue);
}
而BloodValueChange则通过Tween插件来做:
public void BloodValueChange(float newBloodValue)
{
if (newBloodValue == bloodValue) return;
DOTween.To (() =>bloodSlider.value, x => bloodSlider.value = x, newBloodValue, timeSlot);
}
有了控制血条的方法后,我们还希望将血条的展现做的更真实一点。这里我做的主要是模拟3D血条,即血条跟随人物,并且有近大远小的效果。
对于血条跟随人物,主要是通过坐标系换算来实现相对位置的固定:
主要代码如下:
void Update () {
Vector2 player2DPosition = Camera.main.WorldToScreenPoint(transform.position);
Vector3 BloodSlotToCamera = GameObject.Find ("Camera").transform.position - BloodSlotWorldPos;
float BloodSlotDIs = BloodSlotToCamera.magnitude;
float BloodSlotLength = 160f - 2 * BloodSlotDIs > 50f ? 160f - 2 * BloodSlotDIs : 50f;
Vector2 addVector = new Vector2(BloodSlotLength, 20f);
Vector3 BloodSlotWorldPos = transform.position + new Vector3 (0f, 2.8f, 0f);
recTransform.position = Camera.main.WorldToScreenPoint (BloodSlotWorldPos);
recTransform.sizeDelta = addVector;
if (player2DPosition.x > Screen.width || player2DPosition.x < 0 || player2DPosition.y > Screen.height || player2DPosition.y < 0)
{
recTransform.gameObject.SetActive(false);
}
else
{
recTransform.gameObject.SetActive(true);
}
}
最后,在角色攻击触发中调用上述方法即可。关于攻击判断的内容在我的上一篇Markdown中,有兴趣可以读读。
从以上内容可以看出,血条近大远小的主要思路就是计算血条在世界坐标系中与Camera的距离并根据距离不断改变sizeDelta。由于UGUI中的控件都是Mesh加贴图渲染而成,直接操作sizeDelta会导致其Mesh的重建,而大规模非Boss战斗场景中通常血条数量并不少,且由于战斗过程中人物的不断移动会造成血条大小的频繁变化,这种情况下Mesh的重建可能会有昂贵的性能代价,是一种Not good realize。
在这里提供一个启发性的优化思路:不直接操作sizeDelta,而是自己实现一个管理血条大小的方法,可以通过shader操作相关面片的顶点的相对位置来完成。额,这个方法我还在实验当中,如果效果好的话会回来补上代码的。
伤害数值的显示则主要通过TextMesh与一个渐隐动画来实现:
主要代码如下:
void Update ()
{
currentTime = Time.fixedTime - startTime;
if(currentTime > lifespan)
{
Destroy(gameObject);
}
//fade out
if(currentTime > fadeTime)
{
float fade = (fadeTime -(currentTime-fadeTime)) * (1/fadeSpeed);
Color tempColor = yellow.color;
tempColor.a = fade;
yellow.color = tempColor;
tempColor = black.color;
tempColor.a = fade;
black.color = tempColor;
}
//scale and lift accordingly
if(transform.localScale.x < maxSize)
{
transform.localScale = new Vector3 (1.0f,1.0f,1.0f) * (0.01f + currentTime) * 3.0f;
}
transform.position += (Vector3.up * Time.deltaTime * speed);
transform.LookAt(Camera.main.transform.position);
yellow.text = text;
black.text = text;
}
最后,也在角色控制的攻击触发函数中调用此方法即可