zoukankan      html  css  js  c++  java
  • 用uGUI开发自定义Toggle Slider控件

    一、前言

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

    uGui Toggle Slider

    二、制作过程

    完整工程托管于github,分为uGuiNGUI两个project。考虑到版权问题,工程里不含NGUI,同学们需自行将NGUI导进工程。NGUI需要Unity 4.5,uGui需要Unity 4.6。

    三、功能点

    • 滑块可以拖动,从一边拖到另一边将改变控件值。
    • 用户停止操作时,滑块如果居中,会自动滑向最近的一边。
    • 点击滑块或整个控件,控件值将被改变,滑块自动滑向另一边。
    • 控件值被其它脚本修改时,滑块自动滑向另一边。
    • 滑块移动的过程中,如果值发生变化,滑块会以当前位置为起点滑回去。

    下面以uGui为例简述制作方法,NGUI的方法也差不多,两者的区别可参考下文[和NGUI对比]。

    四、Hierarchy

    uGui Toggle Slider

    上图是用uGui制作好的层级结构。其中,

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

    五、Toggle Slider GO

    uGui Toggle Slider GO

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

    六、Animation

    uGui Animation

    所有效果都使用Animation组件实现,全部用动画是为了偷懒,毕竟效果怎么实现都可以,这里仅作演示。动画包含四条曲线,分别用于控制两只twitter小鸟、蓝色背景透明度和滑块左右移动。这里简单提几个要点。

    • 动画的反向播放只需要将AnimationState.speed设为-1。
    • 拖拽滑块时,动画暂停,根据鼠标位移逐帧设置动画时间,然后Sample动画。拖拽停止时恢复动画。
    • 在动画里改变透明度时,Image组件不会自动更新,需要添加一个ColorWatcher组件,自己触发Image.color的setter。
    • 动画设为ClampForever,因为Once无法在AnimationState中保留最后一帧的状态。

    七、Event

    uGui Handle Go

    事件使用两个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 Toggle Slider

    • 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占用就能知道答案。

    文献资料

    本文作者:Jare @ 梦加网络

    本文转载自https://github.com/jaredoc/unity-ugui/tree/master/toggle_demo

  • 相关阅读:
    ASP.NET Core的配置信息
    ASP .NET Core 建立列表和表单View
    ASP.NET Core 如何使用Mvc相关技术建立Controller、Tag Helper (下)
    MySQL日志突然暴涨
    MySQL函数索引及优化
    MySQL统计库表大小
    MySQL8.0窗口函数实践及小结
    MySQL按指定字符合并及拆分
    分享2个近期遇到的MySQL数据库的BUG案例
    mysql大表在不停机的情况下增加字段该怎么处理
  • 原文地址:https://www.cnblogs.com/zhaoqingqing/p/3968818.html
Copyright © 2011-2022 走看看