用uGUI开发自定义Toggle Slider控件
一、前言
写完《Unity4.6新UI系统初探》后,我模仿手机上的UI分别用uGui和NGUI做了一个仅用作演示的ToggleSlider,我认为这个小小的控件已能体现自定义控件的开发过程。由于手头上没有mac版,暂时未能真机测试,PC上的效果如下:

二、制作过程
完整工程托管于github,分为uGui和NGUI两个project。考虑到版权问题,工程里不含NGUI,同学们需自行将NGUI导进工程。NGUI需要Unity 4.5,uGui需要Unity 4.6。
三、功能点
- 滑块可以拖动,从一边拖到另一边将改变控件值。
- 用户停止操作时,滑块如果居中,会自动滑向最近的一边。
- 点击滑块或整个控件,控件值将被改变,滑块自动滑向另一边。
- 控件值被其它脚本修改时,滑块自动滑向另一边。
- 滑块移动的过程中,如果值发生变化,滑块会以当前位置为起点滑回去。
下面以uGui为例简述制作方法,NGUI的方法也差不多,两者的区别可参考下文[和NGUI对比]。
四、Hierarchy

上图是用uGui制作好的层级结构。其中,
- Canvas负责渲染UI。
- Padding没什么用,只是画了一个边框。
- Toggle Slider是控件的父物体。
- BackgroundAndMask使用ImageMask组件作为SymbolOff的遮罩,同时渲染灰色底图。
- SymbolOff是灰色的twitter小鸟,坐标受动画控制。
- Background_On使用Image组件渲染蓝色高亮底图,Color.alpha受动画控制。
- BackgroundMaskOnly使用ImageMask组件作为SymbolOn的遮罩,并不渲染。
- SymbolOn是蓝色的twitter小鸟,坐标受动画控制。(不用Background_On作遮罩是因为蓝色底图的边缘是半透明的。)
- Handle是正方形滑块,坐标只受动画控制。
- BackgroundAndMask使用ImageMask组件作为SymbolOff的遮罩,同时渲染灰色底图。
- Current Value是下面那个可选框,用于测试Toggle Slider。
- Toggle Slider是控件的父物体。
- Padding没什么用,只是画了一个边框。
- EventSystem可参考上篇文章。
五、Toggle Slider GO

Toggle Slider对象包含的Toggle Slider组件是唯一一个直接和控件有关的脚本。代码可在github查阅,编写起来很简单。
六、Animation

所有效果都使用Animation组件实现,全部用动画是为了偷懒,毕竟效果怎么实现都可以,这里仅作演示。动画包含四条曲线,分别用于控制两只twitter小鸟、蓝色背景透明度和滑块左右移动。这里简单提几个要点。
- 动画的反向播放只需要将AnimationState.speed设为-1。
- 拖拽滑块时,动画暂停,根据鼠标位移逐帧设置动画时间,然后Sample动画。拖拽停止时恢复动画。
- 在动画里改变透明度时,Image组件不会自动更新,需要添加一个ColorWatcher组件,自己触发Image.color的setter。
- 动画设为ClampForever,因为Once无法在AnimationState中保留最后一帧的状态。
七、Event

事件使用两个Event Trigger组件进行响应。一个在Toggle Slider对象里,负责响应OnPointerUp,实现当点击控件时,调用ToggleSlider.Toggle()。另一个在Handle对象里 (如图),负责响应Drag事件,实现当拖动时调用ToggleSlider.OnDrag()。
在此遇到了一个蛋疼的问题,Event Trigger的Drop事件在这里无效,又没有单独的DragEnd事件,因此只好在Handle上增加OnPointerUp事件来监听拖动是否结束。如此一来,Handle的OnPointerUp就会把上层控件的OnPointerUp事件拦截掉……我希望Unity能提供类似冒泡的机制,这样一来我就能在Handle上添加一个脚本,只对拖拽结束进行响应,如果是单击事件就冒泡到上层控件进行处理。
最终我的做法是,Handle的OnPointerUp事件也由ToggleSlider.OnPointerUp()响应,OnPointerUp内部通过dragging标记来判断是拖动结束还是单击。
八、不足
- Event Trigger没有冒泡机制,子控件如果不处理事件,没办法抛给父控件处理。
- ImageMask没能选择alpha threshold。
九、存在的Bug
这段时间的测试遇到过几个问题:
- 经常警告"Material uGUI/Stencil Mask doesn't have stencil ... SendWillRenderCanvases()"。有时会导致Image无法显示,要换过一次Sprite之后才正常。
- 两个Hierarchy内平级并且相邻的ImageMask,都选中DrawImage,结果上面一个会挡住下面一个。需要在两个中间插入一包含CanvasRenderer的GO才行,GO可以deactivated。
- 当我制作NGUI版本时,从uGui复制了一份出来再做修改。做到一半时我发现Hierarchy多了一个不含子物体的副本,当我选中控件时副本会同时被选中。于是我重启Unity,发现Unity已经死锁无法关闭,强制结束后项目损坏,只要一打开就crash,手动删除scene后才恢复正常。估计是我在继承树上混用NGUI/uGui,或者uGui未剔除干净引起的,已向官方反馈。
十、和NGUI对比
作为对比,我也用NGUI的测试版(3.6.4b)做了一样的demo,花了不少时间。uGui的事件问题也在NGUI里遇到了,甚至更严重,此外还有其它问题。

NGUI的padding设置挺繁琐的,uGui只要Rect Transform点下stretch,Left/Top/Right/Bottom全写20就行。
添加padding时,我试着创建一个UIWidget,然后设置Anchors为Unified,然后依次设置Left/Top/Right/Bottom为Target's Left/Top/Right/Bottom,然后数值填入20/-20/20/-20才行。
NGUI添加Toggle有点复杂,uGui只要Hierarchy里Create一个就完事了。
创建调试用的Current Value时,找不到NGUI的Toggle组件,后来输入名称才找到,但还是不太会用。后来想到Examples里有toggle的prefab可以用,拖进Scene后对比了下发现NGUI的实现方式比uGui复杂了些,难以手工创建出来。看来Project里要把NGUI这些常见库都备好才行。
NGUI设置Anchor有点失败
将Toggle的prefab实例化到scene里后,设置了很久都没能让Toggle自动居中。难道这个Toggle的尺寸如果是动态的,NGUI就没办法自动居中?或许是我对NGUI还不是很了解,最终我只能根据Toggle宽度算出坐标偏移。
NGUI没有Image Mask
所以这个版本没能加入那两个twitter的logo。这个怪不了NGUI,因为Unity的free版不提供访问stencil buffer的功能,因此第三方UI插件没办法实现比较好的clipping机制。
NGUI的UIEventTrigger无法获得事件参数
UIEventTrigger和uGui的EventTrigger类似,能够触发远程方法。但是NGUI不能传入动态事件参数,虽然能用 UIEventTrigger.current获得当前事件,可UIEventTrigger对象其实没定义任何参数。要获得参数,只能自己写一个带有 OnDrag的组件,附加到GameObject上,或者使用UIEventListener,总之就不支持可视化编辑,只能用代码动态绑定事件。
NGUI的UIEventListener无法响应停止拖动事件
为了解决前一个问题,我使用了UIEventListener来获得拖动参数。然而当我想响应停止拖动事件时,我发现还是要用回UIEventTrigger才行。如果用户不希望混用这两个脚本,那么只能自己写一个脚本。
十一、小结
uGui功能和用户体验方面都做的不错,是我看到过最贴近Unity风格的UI系统。稳定性方面有小问题,不过作为测试版可以理解,已经超过了我的预期(之前以为会和4.0的刚推出Mecanim一样bug一堆)。
性能方面,两个工程我都实现了相同的PackedBenchMark场景,里面各包含了30个ToggleSlider,为了公平uGUI版本去掉了所有ImageMask,两边实测drawcall一致。从帧率上看在编辑器下NGUI性能优于uGUI大约20%!估计是因为NGUI在最近几个版本中完善了batching机制,而uGUI并没有采用前一篇文章所说的"更优的"batch算法,而是把batching粗暴的交给了显卡驱动完成。如果有pro版的话使用profiler查看一下两边的CPU/GPU占用就能知道答案。
文献资料
本文转载自https://github.com/jaredoc/unity-ugui/tree/master/toggle_demo
用uGUI开发自定义Toggle Slider控件的更多相关文章
- 在 Visual C++ 中开发自定义的绘图控件
本文讨论的重点介于两者 之间 — 公共控件赋予您想要的大部分功能,但控件的外观并不是您想要的.例如,列表视图控件提供在许多视图风格中显示数据列表的方式 — 小图标.大图标.列表和详细列表(报告).然而 ...
- kettle系列-[KettleUtil]kettle插件,类似kettle的自定义java类控件
该kettle插件功能类似kettle现有的定义java类插件,自定java类插件主要是支持在kettle中直接编写java代码实现自定特殊功能,而本控件主要是将自定义代码转移到jar包,就是说自定义 ...
- [WPF]Slider控件常用方法
WPF的Slider控件继承自RangeBase类型,同继承自RangeBase的控件还有ProgressBar和ScrollBar,这类控件都是在一定数值范围内表示一个值的用途. 首先注意而Rang ...
- UWP开发随笔——UWP新控件!AutoSuggestBox!
摘要 要开发一款优秀的application,控件肯定是必不可少的,uwp就为开发者提供了各种各样的系统控件,AutoSuggestBox就是uwp极具特色的控件之一,也是相对于之前win8.1的ua ...
- C# Winform 通过FlowLayoutPanel及自定义的编辑控件,实现快速构建C/S版的编辑表单页面
个人理解,开发应用程序的目的,不论是B/S或是C/S结构类型,无非就是实现可供用户进行查.增.改.删,其中查询用到最多,开发设计的场景也最为复杂,包括但不限于:表格记录查询.报表查询.导出文件查询等等 ...
- iOS开发UI篇—UITableview控件基本使用
iOS开发UI篇—UITableview控件基本使用 一.一个简单的英雄展示程序 NJHero.h文件代码(字典转模型) #import <Foundation/Foundation.h> ...
- iOS开发UI篇—UITableview控件使用小结
iOS开发UI篇—UITableview控件使用小结 一.UITableview的使用步骤 UITableview的使用就只有简单的三个步骤: 1.告诉一共有多少组数据 方法:- (NSInteger ...
- SNF开发平台WinForm之三-开发-单表选择控件创建-SNF快速开发平台3.3-Spring.Net.Framework
3.1运行效果: 3.2开发实现: 3.2.1 这个开发与第一个开发操作步骤是一致的,不同之处就是在生成完代码之后,留下如下圈红程序,其它删除. 第一个开发地址:开发-单表表格编辑管理页面 http: ...
- 【jQuery UI 1.8 The User Interface Library for jQuery】.学习笔记.7.Slider控件
默认slider的安装启用 为slider自定义风格 修改配置选项 创建一个垂直的slider 设置最大最小值,和默认值 启用多个 手柄 和 范围 slider内置的回调事件 slider的方法 这个 ...
随机推荐
- Afinal
1.注解功能 1)继承:FinalActivity ( 需要复制 afinal_0.5.1_bin.jar到lib下) 2)@ViewInject() public class AfinalActiv ...
- CSS应用内容补充及小实例
一.clear 清除浮动 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&q ...
- Eclipse 出现Some sites could not be found. See the error log for more detail.错误 解决方法
Eclipse 出现Some sites could not be found. See the error log for more detail.错误 解决方法 Some sites could ...
- javascript函数中的三个技巧【三】
技巧三: [函数绑定] 在javascript与DOM交互中经常需要使用函数绑定,定义一个函数然后将其绑定到特定DOM元素或集合的某个事件触发程序上,绑定函数经常和回调函数及事件处理程序一起使用,以便 ...
- android 不一样的学习记录
http://blog.csdn.net/innost/article/details/48228651 ( 深入理解Android 之 Gradle) 介绍:这篇文章篇幅较长,需要有时间并足够有耐心 ...
- 初学Node(四)事件循环
Node中的事件循环 事件循环是Node的核心,正是因为有了事件循环JS才能够在服务端占有一席之地.JS是一种单线程语言,但是它的执行环境是多线程的在加上JS的事件驱动这一特点,使使JS在执行的过程中 ...
- ASP.NET MVC 微信公共平台开发之 微信接入
ASP.NET MVC 接入微信公共平台 申请微信公共账号 既然要接入微信公共平台,微信公共号是必须的(当然如果只是测试的话也可以申请微信公共平台接口测试账号),来这里微信公共平台 申请微信公共号(注 ...
- 显示所有SAP图标的ABAP代码
TABLES: icon. INCLUDE <icon>. FIELD-SYMBOLS: <f>. SELECT * FROM icon. ASSIGN (icon-name) ...
- R语言学习笔记:SQL操作
虽然R很强大,但如果对SQL非常熟悉,也不能浪费这项技能了,可以用上sqldf包,从example("sqldf")抄了几条用法放在这里,以后可能会用上. library(&quo ...
- 更轻量的 View Controllers
iew controllers 通常是 iOS 项目中最大的文件,并且它们包含了许多不必要的代码.所以 View controllers 中的代码几乎总是复用率最低的.我们将会看到给 view con ...