用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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM