国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

NGUI所見即所得之UICamera

2019-11-09 17:31:03
字體:
來源:轉載
供稿:網友

NGUI所見即所得之UICamera

 

       UI,除了界面的顯示,還有一個重要的元素:事件響應。MoneBehaviour提供一些事件提供了一些函數接口(OnMouseUp,OnMouseDown等),只要MonBehaviour的子類實現這相應的方法以及方法執行的條件達到,Unity底層就會分發調用執行這個函數。一般地,UI事件響應處理機制會有4個基本元素:

 

      1.event object:事件對象,即當前事件的類型,如鼠標左鍵按下等。

      2.event source:事件源,或事件的觸發器,比如說,鼠標左鍵單擊點擊一個button,那么button就是event source,鼠標左鍵單擊就是event source。

      3.event handle:事件處理方法。

      4.event listener:事件的監聽,比如上面說的鼠標左鍵點擊一個button,event listener就是監聽打button的一個mouse click事件,然后分發調用對應的event handle進行處理。

 

        一般event source不會單獨存在,經常會跟event handle綁定在一起,其實就是指定了不同的event handle就是不同event source,如Button就有單擊雙擊事件,Input就有輸入焦點事件,event listener就好像人的大腦,監控這個所有的事件,同時作出不同的響應。

     NGUI自己組織了一套UI事件響應處理機制, 不是對MonoBehaviour的方法的封裝調用,UICamera就是NGUI框架中的event listener,原理很簡單:在Update中捕獲鼠標,鍵盤等設備的輸入(也可以狹義的認為UICamera就是event listener),判斷不同event object 和event source,然后“廣播”分發執行event handle,下面附上分發的函數:

C#代碼  收藏代碼/// <summary>  /// Generic notification function. Used in place of SendMessage to shorten the code and allow for more than one receiver.  /// </summary>    static public void Notify (GameObject go, string funcName, object obj)  {      if (go != null)      {          go.SendMessage(funcName, obj, SendMessageOptions.DontRequireReceiver);            if (genericEventHandler != null && genericEventHandler != go)          {              genericEventHandler.SendMessage(funcName, obj, SendMessageOptions.DontRequireReceiver);          }      }  }  

       知道的誰是event listener,那要怎么實現event handle。實現NGUI的事件的方法有很多種,③給了三種方式監聽NGUI的事件方法。文末貼了NGUI支持的event handle。

                                                                                                                                                                                                                   增補于:11/10/2013 8:45,感覺之前的沒頭沒尾的,就加了上面的鋪墊

 

       記得剛開始用NGUI的時候,就有心思要去琢磨下UICamera,那個時候NGUI還是2.6的版本,現在已經到了3.0.3f,NGUI更新真的很強勁, 當然改動也挺大的,特性也越來越多了。之前本來研究下UICamera最后還是放棄了,因為UICamera的代碼太復雜了,很凌亂,也就放下去了,這幾天重新翻看了下,發現UICamera的可讀性太強了,代碼的組織邏輯很強,完全可以當做文本來從上到下來閱讀,所以才會有這篇文章。

       UICamera做了很多有優化,新增了一些特性,之前可能覺得NGUI只是做一個工具,現在越來越完美了,少廢話,下面把看到的亮點呈上。

ClickNotification

C#代碼  收藏代碼       /// <summary>  /// Whether the touch event will be sending out the OnClick notification at the end.  /// </summary>    public enum ClickNotification  {      None,      Always,      BasedOnDelta,  }  

       ClickNotification定義了OnClick響應的條件,后面也定義了ClickNotification變量 public ClickNotification clickNotification = ClickNotification.Always;

       ClickNotification.None: 不響應OnClick事件

       ClickNotification.Always:總是響應OnClick事件

       ClickNotification.BaSEOnDelta:依據移動的delta的距離判斷是否響應OnClick函數,如果移動距離大于float click  = isMouse ? mouseClickThreshold : touchClickThreshold;則不響應OnClick事件

下面這部分代碼是當響應了OnDrag事件就把currentTouch.clickNotification = ClickNotification.None;就不在會響應OnClick事件了。

C#代碼  收藏代碼                                          bool isDisabled = (currentTouch.clickNotification == ClickNotification.None);  Notify(currentTouch.dragged, "OnDrag", currentTouch.delta);  isDragging = false;    if (isDisabled)  {      // If the notification status has already been disabled, keep it as such      currentTouch.clickNotification = ClickNotification.None;  }  else if (currentTouch.clickNotification == ClickNotification.BasedOnDelta && click < mag)  {      // We've dragged far enough to cancel the click      currentTouch.clickNotification = ClickNotification.None;  }  

 然后再執行OnClick和OnDoubleClick事件先判斷條件currentTouch.clickNotification != ClickNotification.None 是否成立:

C#代碼  收藏代碼// If the touch should consider clicks, send out an OnClick notification                      if (currentTouch.clickNotification != ClickNotification.None)                      {                          float time = Time.realtimeSinceStartup;                            Notify(currentTouch.PRessed, "OnClick", null);                            if (currentTouch.clickTime + 0.35f > time)                          {                              Notify(currentTouch.pressed, "OnDoubleClick", null);                          }                          currentTouch.clickTime = time;                      }  

 EventType

C#代碼  收藏代碼public enum EventType      {          World,  // Perform a Physics.Raycast and sort by distance to the point that was hit.          UI,     // Perform a Physics.Raycast and sort by widget depth.      }  

        這個很簡單就是定義當前射線和碰撞體碰撞的判斷標準,如果是UI則以Depth來判斷,如果是World是以實際距離來判斷。

List<UICamera> list

C#代碼  收藏代碼       /// <summary>  /// List of all active cameras in the scene.  /// </summary>    static public List<UICamera> list = new List<UICamera>();  

        UICamera在初始化的時候會被加入 mList這個鏈表中,然后對鏈表進行排序,根據相機的深度,深度值越小的相機排位靠前,最靠前的相機為場景的主UICamera,然后只有只有主UICamera才會去監測場景中的事件,其他的UICamera并不執行監測任務。UICamera利用Unity的Raycast去監測事件發生的對象,因為發射出去的Ray對象必須碰撞到Collider才會有反應,所以NGUI中所有需要響應事件的控件均需要添加Collider,同時Ray只會碰撞到深度最小的Collider,Ray射線的最大深度為rangeDistance,當這個值為-1時則發射深度和相機深度一樣,主UICamera每一幀都會主動去發射Ray檢測鼠標此時觸碰到的對象并將其記錄在對應的鼠標按鍵事件中,這是能監測到OnHover這個動作的關鍵(當然只有在useMouse為true時才會有此操作)。

        在游戲場景初始化階段,每個UICamera都會根據平臺義useMouse、useTouch、useKeyboard和useController 這些屬性,分別對應的是能否在場景使用鼠標、觸摸屏、鍵盤以及搖桿。

C#代碼  收藏代碼       public bool useMouse = true;    public bool useTouch = true;    public bool allowMultiTouch = true;    public bool useKeyboard = true;    public bool useController = true;  

 

MouseOrTouch

C#代碼  收藏代碼       /// <summary>  /// Ambiguous mouse, touch, or controller event.  /// </summary>    public class MouseOrTouch  {      public Vector2 pos;             // Current position of the mouse or touch event      public Vector2 delta;           // Delta since last update      public Vector2 totalDelta;      // Delta since the event started being tracked        public Camera pressedCam;       // Camera that the OnPress(true) was fired with        public GameObject current;      // The current game object under the touch or mouse      public GameObject pressed;      // The last game object to receive OnPress      public GameObject dragged;      // The last game object to receive OnDrag        public float clickTime = 0f;    // The last time a click event was sent out        public ClickNotification clickNotification = ClickNotification.Always;      public bool touchBegan = true;      public bool pressStarted = false;      public bool dragStarted = false;  }  

       MouseOrTouch是一個很重要的類,是一個事件的結構體,然后就定義了不同平臺的事件,記錄Camera監測的事件:MouseOrTouch只是記錄“鼠標”等的移動的“物理”信息——位置,移動距離等,只有鼠標是否按下只有在Update中每幀監測。

       下面定義不同平臺的事件,例如鼠標事件,mMouse記錄鼠標左鍵,右鍵和中鍵的事件(因為鼠標這里只記錄鼠標的三個按鍵,所以mMouse才是有三個元素,現在明白為啥了吧)。

C#代碼  收藏代碼       // Mouse events  static MouseOrTouch[] mMouse = new MouseOrTouch[] { new MouseOrTouch(), new MouseOrTouch(), new MouseOrTouch() };    // The last object to receive OnHover  static GameObject mHover;    // Joystick/controller/keyboard event  static MouseOrTouch mController = new MouseOrTouch();    // Used to ensure that joystick-based controls don't trigger that often  static float mNextEvent = 0f;    // List of currently active touches  static Dictionary<int, MouseOrTouch> mTouches = new Dictionary<int, MouseOrTouch>();  

 currentTouch

C#代碼  收藏代碼       /// <summary>  /// ID of the touch or mouse Operation prior to sending out the event. Mouse ID is '-1' for left, '-2' for right mouse button, '-3' for middle.  /// </summary>    static public int currentTouchID = -1;    /// <summary>  /// Current touch, set before any event function gets called.  /// </summary>    static public MouseOrTouch currentTouch = null;  

       currentTouch這個變量是整個UICamera中控制事件監測的關鍵所在,記錄了當前事件的觸發對象和一些其他諸如position位置、dealta時間、totaldealta總時間等屬性,然后用currentTouchID記錄當前事件的類型,這些類型包括鼠標事件、鍵盤控制器事件以及觸摸屏事件。

 

ProcessTouch

       ProcessTouch這個函數就是根據currentTouch來針對不同的情況響應不同的函數,被ProcessMouse,ProcessTouch和ProcessOthers調用,如ProcessMouse,分別捕獲鼠標三個按鍵的狀態,然后調用ProcessTouch來響應:

C#代碼  收藏代碼              // Process all 3 mouse buttons as individual touches  if (useMouse)  {      for (int i = 0; i < 3; ++i)      {          bool pressed = Input.GetMouseButtonDown(i);          bool unpressed = Input.GetMouseButtonUp(i);            currentTouch = mMouse[i];          currentTouchID = -1 - i;            // We don't want to update the last camera while there is a touch happening          if (pressed) currentTouch.pressedCam = currentCamera;          else if (currentTouch.pressed != null) currentCamera = currentTouch.pressedCam;            // Process the mouse events          ProcessTouch(pressed, unpressed);      }  

『ProcessMouse分析

         因為之前版本升級到NGUI3.0.6時,UICamera出現了一個Bug:當Time.ScaleTime != 1f 的時候,事件響應有問題,當時由于時間關系,只是和之前的版本進行比對,增加了些代碼解決的。但是還是感覺沒有能對UICamera具體細節沒能完全掌握,挺蹩腳的,還不能達到“自主”的處理目的,所以一直都想有時間好好把UICamera的事件分發流程細節清理下。

C#代碼  收藏代碼       /// <summary>  /// Update mouse input.  /// </summary>    public void ProcessMouse ()  {      // No need to perform raycasts every frame      if (mNextRaycast < RealTime.time)     //更新鼠標current為當前的 hoveredObject,如果時間間隔小于 20毫秒,就不更新      {          mNextRaycast = RealTime.time + 0.02f;          if (!Raycast(Input.mousePosition, out lastHit)) hoveredObject = fallThrough;          if (hoveredObject == null) hoveredObject = genericEventHandler;          for (int i = 0; i < 3; ++i) mMouse[i].current = hoveredObject;      }        lastTouchPosition = Input.mousePosition;      bool highlightChanged = (mMouse[0].last != mMouse[0].current);      if (highlightChanged) currentScheme = ControlScheme.Mouse;        // Update the position and delta                  更新三個鼠標按鍵的位置 delta 和pos ,      mMouse[0].delta = lastTouchPosition - mMouse[0].pos;      mMouse[0].pos = lastTouchPosition;      bool posChanged = mMouse[0].delta.sqrMagnitude > 0.001f;        // Propagate the updates to the other mouse buttons      for (int i = 1; i < 3; ++i)          {          mMouse[i].pos = mMouse[0].pos;          mMouse[i].delta = mMouse[0].delta;      }        // Is any button currently pressed?      bool isPressed = false;        for (int i = 0; i < 3; ++i)      {          if (Input.GetMouseButton(i))          {              currentScheme = ControlScheme.Mouse;              isPressed = true;              break;          }      }        if (isPressed)      {          // A button was pressed -- cancel the toolt  這次回看UICamera的代碼,更加UICamera優化了很多,代碼邏輯清晰簡單了,之前的一直感覺很亂(一堆條件判斷)才一直沒有細看。雖然上面代碼還是有加點注釋,其實已經完全沒必要了。然后在NGUI3.0.7版本還增加了 在Editor下用鼠標做屏幕Touch的操作的功能:

/// Process fake touch events where the mouse acts as a touch device.

/// Useful for testing mobile functionality in the editor.』

                                                                                                                                                                                                                    增補于 2013,12,29 15:15

其他

        UICamera還提供其他一些“特性”,能夠讓開發者實現更多的功能(就不解釋了吧, 有注釋):

C#代碼  收藏代碼/// <summary>      /// If 'true', once a press event is started on some object, that object will be the only one that will be      /// receiving future events until the press event is finally released, regardless of where that happens.      /// If 'false', the press event won't be locked to the original object, and other objects will be receiving      /// OnPress(true) and OnPress(false) events as the touch enters and leaves their area.      /// </summary>        public bool stickyPress = true;    /// <summary>      /// If set, this game object will receive all events regardless of whether they were handled or not.      /// </summary>        static public GameObject genericEventHandler;        /// <summary>      /// If events don't get handled, they will be forwarded to this game object.      /// </summary>        static public GameObject fallThrough;  

 

最后,NGUI一共支持一下事件:

 

C#代碼  收藏代碼void OnHover (bool isOver) – Sent out when the mouse hovers over the collider or moves away from it. Not sent on touch-based devices.  void OnPress (bool isDown) – Sent when a mouse button (or touch event) gets pressed over the collider (with ‘true’) and when it gets released (with ‘false’, sent to the same collider even if it’s released elsewhere).  void OnClick() — Sent to a mouse button or touch event gets released on the same collider as OnPress. UICamera.currentTouchID tells you which button was clicked.  void OnDoubleClick () — Sent when the click happens twice within a fourth of a second. UICamera.currentTouchID tells you which button was clicked.  void OnSelect (bool selected) – Same as OnClick, but once a collider is selected it will not receive any further OnSelect events until you select some other collider.  void OnDrag (Vector2 delta) – Sent when the mouse or touch is moving in between of OnPress(true) and OnPress(false).  void OnDrop (GameObject drag) – Sent out to the collider under the mouse or touch when OnPress(false) is called over a different collider than triggered the OnPress(true) event. The passed parameter is the game object of the collider that received the OnPress(true) event.  void OnInput (string text) – Sent to the same collider that received OnSelect(true) message after typing something. You likely won’t need this, but it’s used by UIInput  void OnTooltip (bool show) – Sent after the mouse hovers over a collider without moving for longer than tooltipDelay, and when the tooltip should be hidden. Not sent on touch-based devices.  void OnScroll (float delta) is sent out when the mouse scroll wheel is moved.  void OnKey (KeyCode key) is sent when keyboard or controller input is used.  

 

小結:

       最近由于項目要用到FastGUI,然后手上的FastGUI不支持NGUI(NGUI變動太大了),然后自己要升級下FastGUI,就要更多的掌握NGUI的原理,所以才會一直不斷的寫一些文章。寫文章主要是記錄下自己從中看到的東西,當然D.S.Qiu最喜歡和大家分享,希望能對讀者有幫助,哪怕只有一個人,D.S.Qiu也會很興奮的,因為很多次D.S.Qiu都不打算寫的(文章寫的太爛,沒有深度,邏輯差,每次都要熬夜等),但當我看到別人文章的亮點時,我就覺得自己還是可以分享些的。

       今天把FastGUI 兼容到了NGUI3.0.3f,還增加一些功能,然后要寫一個文檔給美術的同事,我感覺頭就大了,感覺如果要我口述一定能讓聽者完全明白,但是寫起來就完全不著調,所以覺得D.S.Qiu的文字很渣,馬上就是凌晨1:30,睡覺,晚安!

 

        如果您對D.S.Qiu有任何建議或意見可以在文章后面評論,或者發郵件(gd.s.qiu@Gmail.com)交流,您的鼓勵和支持是我前進的動力,希望能有更多更好的分享。

        轉載請在文首注明出處:http://dsqiu.iteye.com/blog/1971866

更多精彩請關注D.S.Qiu的博客和微博(ID:靜水逐風)

 

 

參考:

①2B青年: http://blog.sina.com.cn/s/blog_6f16aba701017mgz.html

②tasharen: http://www.tasharen.com/?page_id=160

③雨松MOMO:http://www.xuanyusong.com/archives/2390


上一篇:Unity問題集合

下一篇:apk的安裝分析

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 石嘴山市| 深州市| 连城县| 湘潭县| 揭西县| 牡丹江市| 商河县| 岫岩| 米脂县| 霸州市| 京山县| 顺昌县| 奉新县| 肥西县| 丰镇市| 奇台县| 柳江县| 太和县| 广河县| 嘉鱼县| 合阳县| 北票市| 高要市| 施甸县| 宝鸡市| 堆龙德庆县| 喀什市| 三穗县| 长沙县| 沂南县| 龙海市| 疏勒县| 仪征市| 肃南| 乌恰县| 朝阳区| 临城县| 莒南县| 巴彦县| 梨树县| 濮阳县|