曾经在项目开发中,遇到有的时候UI不是用UGUI,比如SpriteRenderer,甚至有些项目UI是3D的,也有时候UGUI Button的一些功能不能满足我们的需求,比如Button的交互是特效或者是几个UI组成的一种交互方式,那么UGUI自带的Button其实并不能满足我们的需求;
一、思路解析
不管是UGUI的Button思路,还是任何我们常见的Button,其实操作都是一样的,那么我们就来解析下UGUI Button的实现机制。
其实,最初我在开发中,我以为UGUI的Button实现机制是通过射线Ray去实现的(好像NGUI是通过射线Ray来实现的),后来我在开发中又发现了,UGUI有可能是通过屏幕坐标去识别控件的区域,然后来实现Button的操作。
这两种情况其实都是可以实现的,有好有坏,在有些情况下射线Ray会比屏幕坐标识别区域来好,有些情况屏幕坐标识别区域会比射线Ray要方便很多,屏幕坐标就不用管这个物体是谁,控件只需要断鼠标位于区域内,就可以执行操作,这个因情况而定。
当然,以上的两种情况只是我的猜测,因为我还没有吃透UGUI,估计我了解的只是浅层的东西吧,不过我也基于这两种情况,让我自己基于UGUI重新写了一套新的GUI。
Button控件,重点需要的事件有下面几种:
1)、OnEnter(移入)
2)、OnExit(移出)
3)、OnDown(按下)
4)、OnUp(抬起)
5)、OnClick(点击)
以上的五种Button常用的事件,基本上是所有Button公用的事件了,当然还有其他的,比如双击(其实就是在指定时间内,执行两次OnClick)、长按(其实就是OnDown按下多久,也是时间参数)等。
以下实现原理是通过射线Ray来实现的,至于屏幕坐标识别控件,会在后续其他控件(滚动条ScrollBar)在介绍,可能会更好一些。
注:下述说的Button介绍原理,并不是UGUI的Button,而是说用一个Image或者GameObject物体,如何来实现Button的机制。
1、OnEnter原理
OnEnter()原理非常简单,就是当射线照射到Button时,触发OnEnter()事件,该事件只会执行一次,也就是移入那一刻触发。
2、OnExit原理
OnExit()原理,当射线离开已经照射到的Button时,触发OnExit()事件,该事件同样执行一次。当移入后移出时触发。
3、OnDown原理
OnDown()原理,前提是执行OnEnter后,也就是射线照射到Button了,然后在这个Button中按下,则触发OnDown()事件。
这里的“按下”,一般是鼠标的按下(至于哪个键按下来触发这个事件,自己定义即可),同时你也可以是VR设备的按键,也可以是任何其他你所定义的设备。
这里就延伸出一个东西了,也就是说输入端(比如鼠标、键盘、VR设备、等其他设备),按下某个键,通过按下某个键来通知Button,然后就可以触发这个Button的事件。
4、OnUp原理
OnUp()原理,前提是执行OnDown后,在该Button中执行释放操作,则触发OnUp()事件。
这里的“释放”,一般是鼠标按下后,释放。也可以是其他外设备某个键按下之后的释放操作。
5、OnClick原理
OnClick()原理,则是OnDown-OnUp。两个事件的组合。当在照射到的Button中执行OnDown事件后,同时在该Button内也执行了OnUp事件。当这一套事件组在指定Button内执行后,这个操作就属于OnClick()事件了。
二、实现
在上述中,介绍了Button的原理后,现在我们就需要去如何实现了,我们需要写两个脚本。第一个脚本是射线控制端(RayController),它是用来发射线,判断照射到哪个物体,以及一些事件的触发。第二个脚本是Button类,也就是提供上述的事件。
Button类
定义上述我们介绍的一些事件。
基于UnityEvent类扩展了ButtonEvent,携带Int参数,这个参数就是按键ID,比如鼠标左键为0,右键为1等等。在外设中,基本每一个按键都是有ID的,即使是VR设备。
在定义用于RayController触发的相关方法。
其他方式也是类似的,在方法内执行事件,用于通知已经注册在内的事件。
同时在指定Button的Layer层,以及Tag。指定Tag方便射线照射物体时可以更省内存去找到指定的Button Layer。如果没有Tag的话,那么当射线照射到该Layer时,要想分辨出Buton,必须用GetConponent来判断是不是Button,如果只执行一次还好,如果每帧都执行,那就会占内存了。
在此,Button类就已经设计完毕了,当然你也可以在Button中添加音频、IsEnable(Button的激活)等等。
RayController
射线控制端就相对简单了,需要注重三个点,第一个点接收【外设键按下】、【外设键释放】、【射线照射】。
外设键按下
上述的OnButtonPress()方法是用于接收外设键的按下处理,当有键按下时,判断此时是否照射到Button(currentButton),如果currentButton不为Null,则执行OnDown方法,并且将键ID作为参数进行传输。
至于“handIndex!=this._handIndex”这行代码则是用于屏蔽射线发射点,比如双鼠标、或者VR的两只手柄,如果手柄一照射到物体进行按下,那么应该只触发手柄一的按下处理,手柄二的一些处理则不应该执行。
外设键释放
OnButtonRelease方法是键释放时的处理,释放时处理有几种情况,一种是在在Button中执行了OnDown,但是移动了其他地方,这时只执行释放OnUp。另一种是在Button中执行了OnDown,在该Button执行了OnUp。那么OnUp事件还是要触发,同时还要执行OnClick。因为这一套操作组合成了一个单击操作。
如果想执行双击,那么只需要在指定时间内接收OnClick次数
射线处理
1、定义摄像机和发射源
发射源需要注意的是,是从哪里开始发射线,这里是鼠标。如果是VR设备,那么就是手柄了。一般任何的VR设备都会有关于手柄坐标的接口,这个可以不用担心。
2、照射指定Layer
通过发射点,照射到指定层,然后判断照射到的是不是Button,如果是则进行一些处理,如果不是,则进行其他处理。
3、对Button进行处理
这里主要是当照射是Button时,则实现OnEnter,如果照射到同一个则不处理,如果照射到不同的,则把当前的Button执行OnExit。并且设置另一个Button执行OnEnter。这一块就是逻辑的处理,很简单。
三、总结
到此,Button的原理介绍就讲完了,Button相对来说,比较简单。只要稍微观察下Unity的Button就可以知道了。当然上述的Button只是最初形态,在我开发的GUI中,这个属于Button的基类,通过它我延伸出Toggle、Dropdown、Scrollbar。因为上述的Button是通过射线来做处理的,所以不管是3D物体,还是UGUI中的UI,都是可以适用的。
首页就是我最终的Button了,根据选择不同的Button类型(SpriteRender、Image、Object)等来进行操作。同时还有Button组、音频等等。
下一篇我会介绍Button组、Toggle(开关)的原理。
读者如果有任何的疑问或意见可以直接关注我的公众号(Hua灬清)进行询问,同时每周会推送原创技术博客。
网友评论