Unity3D战斗系统中的血条表现与伤害判定

Blood Slot Display and Damage

Introduction

在战斗系统中,血条显示必不可少,而伤害的浮动数值表现也与血条相辅相成。这个DEMO主要实现了简单的战斗中血条表现与伤害数值浮动显示

Blood Slot

在Unity3D中可以用UGUI中的Slider简单实现一个血条UI:

  • 创建一个Canvas与Slider,并删除Slider中的Handle Slide Area
  • 设置Target Grahpic为Fill组件,并选择想要的血条覆盖色(如红色)
  • 设置Slider最大值
  • 修改Background与Fill的Rect Transform,全部置0,保证覆盖色能覆盖整个血条
  • 设置覆盖方向(Left to Right or oppsite)
  • 拖动Value条即可看见血条变化

有了血条UI后,接下来的工作就是将血条与角色结核。这里可以直接在角色的Class中添加一个UI的Slider变量然后绑定到一个Slider控件上,也可以封装一层Blood类当做控制器用于管理血条,解耦合,避免角色对于UI控件的直接操作。不管怎么实现,最重要的都是:

  • 实现其中的onIsHited方法来控制血条值
  • 实现BloodValueChange来改变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血条,即血条跟随人物,并且有近大远小的效果。

对于血条跟随人物,主要是通过坐标系换算来实现相对位置的固定:

  • 在角色的Class中设定一个RectTransform成员变量绑定血条的transform
  • 求解世界坐标系中血条的坐标,这里我是让血条的坐标为角色坐标加上Vector3(0,2.8,0)
  • 将血条的世界坐标转换为屏幕坐标
  • 将血条的RectTransform.position设为上一步中的屏幕坐标
  • 在每一帧或鼠标滚轮事件中计算世界坐标中血条与Camera的距离,距离乘系数作为血条的size

主要代码如下:

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中,有兴趣可以读读。

Advance Blood Slot

从以上内容可以看出,血条近大远小的主要思路就是计算血条在世界坐标系中与Camera的距离并根据距离不断改变sizeDelta。由于UGUI中的控件都是Mesh加贴图渲染而成,直接操作sizeDelta会导致其Mesh的重建,而大规模非Boss战斗场景中通常血条数量并不少,且由于战斗过程中人物的不断移动会造成血条大小的频繁变化,这种情况下Mesh的重建可能会有昂贵的性能代价,是一种Not good realize。

在这里提供一个启发性的优化思路:不直接操作sizeDelta,而是自己实现一个管理血条大小的方法,可以通过shader操作相关面片的顶点的相对位置来完成。额,这个方法我还在实验当中,如果效果好的话会回来补上代码的。

Damage Number

伤害数值的显示则主要通过TextMesh与一个渐隐动画来实现:

  • 建立一个HitDisplay类来管理伤害数值显示,其中有两个TextMesh变量用于文本的前景与后景
  • 在Update中利用Time来实现动画
  • 当攻击触发时,先创建一个GameObject用于动画表现
  • 将TextMesh渲染到GameObject上
  • 不断改变GameObject的位置,达到“飘起来”的效果
  • 到达最高点后进行渐隐动画
  • 渐隐完成后Destroy该GameObject

主要代码如下:

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;
}

最后,也在角色控制的攻击触发函数中调用此方法即可

你可能感兴趣的:(Unity)