欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Unity3d Note9(使用UGUI与IMGUI制作血条)

程序员文章站 2022-03-26 23:20:05
...

1.要求

血条(Health Bar)的预制设计。具体要求如下:

  • 分别使用 IMGUI 和 UGUI 实现
  • 使用 UGUI,血条是游戏对象的一个子元素,任何时候需要面对主摄像机
  • 分析两种实现的优缺点
  • 给出预制的使用方法

2.UGUI实现

因为要求使用UGUI时,血条是游戏对象的一个子元素,所以先创建一个游戏对象:

  • 在Hierarchy窗口右击打开上下文菜单,选择3D Object,选择Plane,为游戏对象添加基本的舞台。
  • 点击菜单栏中的Assets,点击Import Package,选择Characters,然后一步步操作引入资源。
  • 展开资源视图Standard Assets::Characters::ThirdPersonCharacter::Prefabs,然后将预制体ThirdPersonController拖入Hierarchy窗口,改名为Ethan。
  • 检查以下属性
    • Plane 的 Transform 的 Position = (0,0,0)
    • Ethan 的 Transform 的 Position = (0,0,0),Rotation = (0,180,0)
    • Main Camera 的 Transform 的 Position = (0,1,-10)
  • 运行检查成果,控制人物来回走动。

然后为人物加上血条

  • 给Ethan添加Canvas子对象:右击Ethan,选择UI,选择Canvas。
  • 选中Ethan的子对象Canvas,在Inspector视图修改Canvas的属性:
    • 将Canvas组件中的Render Mode设为World Space。
    • 将Rect Transform组件的(Pos X, Pos Y, Width, Height)设置为(0,2,160,20)。
    • 将Rect Transform组件中的Scale(X, Y)设置为(0.01, 0.01)。
  • 选择Ethan的子对象Canvas,选择上下文菜单中UI的Slider,添加滑条作为血条子对象。
  • 然后展开Slider:
    • 将Handle Slide Area禁灰。
    • 将Background的Left设为-10,Scale X设为0.88,即将Background设置为血条的边框。
    • 将Fill Area展开,将Fill的Image组件中的Color选为红色。
  • 选择Slider,将Slider组件的Min Value设为0,将Max Value设为100。
  • 运行检查成果

运行后发现血条能跟随游戏对象移动,但是却不能一直朝向主摄像机,这需要给Canvas添加脚本LookAtCamera.cs:

using UnityEngine;

public class LookAtCamera : MonoBehaviour
{
    void Update()
    {
        this.transform.LookAt(Camera.main.transform.position);
    }
}

然后再给角色Ethan的子对象Canvas添加一个血条的控制器HealthControl.cs:

using UnityEngine;
using UnityEngine.UI;

public class HealthControl : MonoBehaviour {
    // 血条
    public Slider mainSlider;
    // 恢复血的时间的间隔
    public float duration = 10;
    // 用于计时
    private float dur_time = 0;
    // 当前血量
    private float health;
    // 增/减后血量
    private float resultHealth;
    // 增减血
    private bool flag = false;

    void Start () {
        // 设置最大血量、最小血量、回血间隔以及现在血量
        mainSlider.maxValue = 100;
        mainSlider.minValue = 0;
        duration = 10;
        // mainSlider.wholeNumbers = true;
        mainSlider.value = mainSlider.maxValue / 2;
        health = mainSlider.value;
        resultHealth = health;
    }

    void Update () {
        // 计时以回血
        if(mainSlider.value < mainSlider.maxValue - 0.1 && !flag)
        {
            dur_time += Time.deltaTime;
            if(dur_time >= duration)
            {
                resultHealth = mainSlider.value + 10 > mainSlider.maxValue ? mainSlider.maxValue : mainSlider.value + 10;
                dur_time = 0;
            }
        }
        // 发生碰撞,减血
        else if (flag)
        {
            resultHealth = mainSlider.value - 20 < mainSlider.minValue ? mainSlider.minValue : mainSlider.value - 20;
            flag = false;
        }
        // 平滑减少血量
        health = Mathf.Lerp(health, resultHealth, 0.05f);
        mainSlider.value = health;
        // 控制slider中Fill Area的显示,以更符合血量的显示
        if (mainSlider.value <= 0.01)
            mainSlider.transform.GetChild(1).localScale = Vector3.zero;
        else
            mainSlider.transform.GetChild(1).localScale = Vector3.one;
    }

    // 外部控制减少血量
    public void ReduceHealth()
    {
        flag = true;
    }
}

然后将血条Slider添加到脚本HealthControl中:
Unity3d Note9(使用UGUI与IMGUI制作血条)
Unity3d Note9(使用UGUI与IMGUI制作血条)
然后检验一下血量控制的脚本:
- 添加一个cube,右键点击Hierarchy窗口打开上下文窗口,选择3D Object,选择Cube,然后给Cube添加一个碰撞检测脚本CubeCollide.cs:

using UnityEngine;

public class CubeCollide : MonoBehaviour {
    private void OnCollisionEnter(Collision collision)
    {
        if(collision.gameObject.name == "Ethan")
        {
            collision.gameObject.GetComponentInChildren<HealthControl>().ReduceHealth();
        }
    }
}
  • 调整cube的大小:在Inspector窗口将Scale X设置为5
  • 运行检测一下:
    Unity3d Note9(使用UGUI与IMGUI制作血条)

3.IMGUI实现

思路:用HorizontalScrollbar(水平滚动条)的宽度作为血条的显示值:
- 右击Hierachy窗口打开上下文菜单,选择Create Empty,重命名为IMGUI_HealthBar。
- 给IMGUI_HealthBar添加脚本IMGUIHealthControl.cs:

using UnityEngine;

public class IMGUIHealthControl : MonoBehaviour {
    // 当前血量
    public float health = 50;
    // 增/减后血量
    private float resultHealth = 0;

    private Rect bloodBar;
    private Rect upButton;
    private Rect downButton;

    void Start () {
        // 初始值
        health = 50;
        resultHealth = 50;
        // 居中
        float w = Screen.width;
        float h = Screen.height;
        // 血条横向
        bloodBar = new Rect((w - 200) / 2, (h - 20) / 2, 200, 20);
        // 加血按钮
        upButton = new Rect(w / 3, 100, 40, 20);
        // 减血按钮
        downButton = new Rect(w / 3 * 2, 100, 40, 20);
    }

    void OnGUI () {
        if (GUI.Button(upButton, "加血"))
            resultHealth = health + 10 > 100 ? 100 : health + 10;
        if (GUI.Button(downButton, "减血"))
            resultHealth = health - 10 < 0.1 ? 0 : health - 10;
        GUI.color = new Color(0.2f, 0.2f, 0.2f, 0.5f);
        health = Mathf.Lerp(health, resultHealth, 0.05f);
        GUI.HorizontalScrollbar(bloodBar, 0, health, 0, 100);
    }
}
  • 运行检测一下:
    Unity3d Note9(使用UGUI与IMGUI制作血条)

4.分析两种实现的优缺点

  • 首先是UGUI,这种方法毫无疑问是非常好用的,因为它即可以用代码控制,而且使用的是面向对象的思维,是可以个性化定制的,比如血条Slider的子对象Background是有一个Image组件的,可以通过Source Image定制血条的样式,换一个边框,换一种样式,这里因为我没有素材就不展示了。此外,由于UGUI的控件放到了控件库中,可以在Hierachy窗口中添加UGUI的控件,而且可以通过Inspector来简单的定制这些控件,而不用使用到代码,每次要运行后才能看到效果。
    按 Unity 官方说法,IMGUI 主要用于以下场景:
    • 在游戏中创建调试显示工具
    • 为脚本组件创建自定义的 Inspector 面板。
    • 创建新的编辑器窗口和工具来扩展 Unity 环境。
  • 然后是IMGUI,IMGUI 的存在符合游戏编程的传统,即使在今天它依然没有被官方宣判为遗留(将要淘汰的)系统(Legacy Systems)。在修改模型,渲染模型这样的经典游戏循环编程模式中,在渲染阶段之后,绘制 UI 界面无可挑剔(参考Execution Order of Event Functions)。这样的编程即避免了 UI 元素保持在屏幕最前端,又有最佳的执行效率,一切控制掌握在程序员手中,这对早期计算和存储资源贫乏的游戏设备来说,更是弥足珍贵。当然,早年 UI 交互手段就是绘制图片和文本,检测输入事件等基本任务。
    IMGUI系统通常不打算用于玩家可能使用并与之交互的普通游戏内用户界面。为此,应该使用 Unity 的基于 GameObject 的 UGUI 系统。
    因此,掌握了解 IMGUI 是非常必要:
    • 新手 UI 入门容易,帮助新手理解引擎的游戏循环
    • 高级程序员,创建在线调试环境
    • 工具开发者,定义 Unity 新的编程工具

5.给出预制的使用方法

前面没有使用预制,在这里得修改一下:

  • 在Project窗口新建文件夹Resources,在Resources里面新建文件夹Prefabs。
  • 然后将Ethan里面的Canvas对象拖曳到Prefabs里面制作成预制体,重命名为UGUI_HealthBar,将IMGUI_HealthBar也拖曳下来。
  • 使用Prefabs里面的UGUI_HealthBar预制*作血条:给Ethan添加LoadResources.cs脚本
using UnityEngine;

public class LoadResources : MonoBehaviour {
    private void OnEnable()
    {
        if(gameObject.name == "Ethan")
        {
            Canvas canvas = Instantiate(Resources.Load("Prefabs/UGUI_HealthBar", typeof(Canvas)), Vector3.zero, Quaternion.identity, null) as Canvas;
            canvas.gameObject.SetActive(true);
            canvas.gameObject.transform.SetParent(this.transform);
        }
        else
        {
            GameObject gameObject = Instantiate(Resources.Load("Prefabs/IMGUI_HealthBar", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
            gameObject.SetActive(true);
        }
    }
}
  • 使用Prefabs里面的IMGUI_HealthBar预制*作血条:在Hierachy窗口添加一个空对象Main,添加脚本LoadResources.cs。
  • 运行检测一下。

最终运行效果是与没有使用预制体的时候是一致的。