1.1 绑定事件响应

请列出为UI控件绑定事件响应的方法,并分析不同方法之间的优劣。

问题分析

面对问题时,最重要的是确定它的边界。对于这个问题,如何绑定当然是关键,但UI控件的范围也很重要。不是所有的UI控件都能绑定事件,但能绑定的也不是只有按钮,至少还应包含:

◎ 拖动条

◎ 输入框

◎ 开关

◎ 滚动区域

当然还有千千万万我们实际工作中用到的自扩展控件。这里不受限于按钮的点击事件,我们用按钮、开关、拖动条三种控件来举例说明。

搭建测试环境

在面临一个新问题时,我们通常会在一个独立的场景中搭建测试环境,来快速验证我们的逻辑是否正确。

在这个例子中,我们要在界面中创建三个控件,并摆放到屏幕的中心。创建方法是用右键快捷菜单添加各种控件,相信大家都知道如何操作。如果不熟悉,也可以通过网络很容易地查到。类似的基础操作不是本书的重点,因此针对此类情况,后文大多一笔带过。创建好的界面大概如图1.1所示。

图1.1

接着,编写处理函数。在Unity中,创建名为TestEvent.cs的代码文件,并在场景中创建一个空的GameObject,将TestEvent脚本挂载到GameObject上。打开

TestEvent.cs,输入如下代码:

    using UnityEngine;
    using System.Collections;
    using UnityEngine.UI;

    public class TestEvent : MonoBehaviour
    {
        public void OnBtnClick()
        {
            Debug.Log("Btn Click");
        }
        public void OnToggleChange(bool isOn)
        {
            Debug.Log("Toggle Is "+isOn);
        }
        public void OnSliderChange(float rate)
        {
            Debug.Log("Slider cur is "+rate);
        }
    }

现在我们已经配置好了测试环境,接下来就看看如何绑定响应。

绑定响应

从操作层面看,绑定响应的方式有两种,但它们的核心是相同的。

1)组件中添加

在Inspector界面中,可以直接在组件中通过拖曳的方式完成绑定。我们以Button控件为例,演示如何操作。

首先创建一个空的GameObject,命名为MyTrigger,在它上面挂载刚刚创建的TestEvent脚本。

完成上述步骤后,在Hierarchy面板中选中Button,然后在Inspector面板中找到Button组件。不难发现在组件的下方有个区域名为OnClick,单击这个区域下方的“+”,就可以添加一个点击事件的处理设置,效果如图1.2所示。

图1.2

然后将刚刚含有TestEvent脚本的GameObject拖到下方的“None (Object)”中。操作完成后,单击右侧的“No Function”按钮,在下拉菜单中找到TestEvent.OnBtnClick。操作成功,效果如图1.3所示。

图1.3

单击“Runtime Only”按钮进行测试,即可发现在Console中可以输出“Btn Click”。

用相同的方式,可以为Toggle和Slider添加相同的事件处理。添加好后,运行游戏,分别测试控件响应,控制台输出如图1.4所示。

图1.4

2)代码中添加

代码添加的方式要简单得多,打开TestEvent脚本,在类中添加如下代码:

    public Button m_btn;
    public Toggle m_toggle;
    public Slider m_slider;

    void Start()
    {
        m_btn.onClick.AddListener(OnBtnClick);
        m_toggle.onValueChanged.AddListener(OnToggleChange);
        m_slider.onValueChanged.AddListener(OnSliderChange);
    }

然后在Inspector中将三个控件分别拖到脚本的对应位置上即可。

优劣比较

以笔者的经验来看,绝大多数程序员喜欢用代码添加的方式。

一般来说,程序员不会轻易动别人的代码,但预设很可能被其他人更改。当Bug出现时,不使用代码编写绑定,开发人员心里就很没底,无法确认是否是自己的原因。

笔者认为如果有很好的框架支撑,编辑器设置绑定的方式也是可取的。就好像没人质疑在Unity 3D中,Start函数是否会在一开始调用一样。如果有一目了然的绑定显示,并且有稳固的框架支撑,那么在编辑器中绑定也没什么不好。在编辑器中绑定有个优势,就是支持动态更新。由于绑定关系存储在预设中,因此意味着整个逻辑可以跟随AssetBundle更新。如果C#代码逻辑写错了,只更新预设也是白费力气,但可以在基类里提供一个默认的出错处理,以便最大限度地降低影响。

总结

绑定响应的方式有两种;在组件中添加和在代码中添加。它们的核心相同,都是通过代理来回调自己的函数。组件绑定的操作更友好,但容易被人更改。代码绑定的优点是更稳定,而且能更灵活地控制监听的时间点,缺点是逻辑编译在代码中,最好有健全的框架支持,以防止出现使用问题。例如,重复绑定的处理函数,忘记移除的绑定,等等。

扩展问题

(1)有没有办法克服代码绑定的缺点?

(2)既然大家都倾向于使用代码绑定,那么组件绑定的存在有意义吗?