NGUI的事件通知架构和源码剖析--UICamera
来源:互联网 发布:mac c库函数 编辑:程序博客网 时间:2024/06/12 10:47
NGUI的事件通知架构和源码剖析
作者:leoleocs
NGUI的事件通知其实是由一个脚本UICamera来实现的,脚本的命名不是太好,其基本的原理很简单,在Update函数中检测用户输入,然后根据自己的策略分发到具体的物体。其定义了一些基本的通知回调函数,你可以查看具体的注释:
/// * OnHover (isOver) is sent when the mouse hovers over a collider or moves away./// * OnPress (isDown) is sent when a mouse button gets pressed on the collider./// * OnSelect (selected) is sent when a mouse button is first pressed on an object. Repeated presses won't result in an OnSelect(true)./// * OnClick () is sent when a mouse is pressed and released on the same object./// UICamera.currentTouchID tells you which button was clicked./// * OnDoubleClick () is sent when the click happens twice within a fourth of a second./// UICamera.currentTouchID tells you which button was clicked./// /// * OnDragStart () is sent to a game object under the touch just before the OnDrag() notifications begin./// * OnDrag (delta) is sent to an object that's being dragged./// * OnDragOver (draggedObject) is sent to a game object when another object is dragged over its area./// * OnDragOut (draggedObject) is sent to a game object when another object is dragged out of its area./// * OnDragEnd () is sent to a dragged object when the drag event finishes./// /// * OnTooltip (show) is sent when the mouse hovers over a collider for some time without moving./// * OnScroll (float delta) is sent out when the mouse scroll wheel is moved./// * OnKey (KeyCode key) is sent when keyboard or controller input is used.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
所以从字面上你就可以理解, 其提供了哪些事件通知,这些事件都是在在主线程中完成的。需要特别说明的是,NGUI有自己的事件通知,和MonoBehavior里面的函数OnMouseDown, OnMouseUp, OnMouseOver等消息处理函数重叠,所以只要我们使用了NGUI的处理框架以及NGUI的脚本,如UIButton,UISCrollView等,我们无需重载MonoBehavior的上述事件处理函数。 如果自己处理了,可能同一个用户输入会响应两次。
下面就简单介绍一下关键的函数或者结构体
- 通知函数, Notify
/// <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 (mNotifying) return; mNotifying = true; if (NGUITools.GetActive(go)) { // 基本的Unity的GameObject函数. go.SendMessage(funcName, obj, SendMessageOptions.DontRequireReceiver); if (mGenericHandler != null && mGenericHandler != go) { mGenericHandler.SendMessage(funcName, obj, SendMessageOptions.DontRequireReceiver); } } mNotifying = false; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
所以,消息通知是通过GameObject::SendMessage ()的方法来实现的,可以查看源码,等到funcName的名字都是上面的事件通知的名字,如: OnClick, OnHover, OnSelect等。
- Raycast 帮助函数,如何从一个屏幕上的位置信息,找到点击,触摸,滑过的物体
/// <summary> /// Returns the object under the specified position. /// </summary> static public bool Raycast (Vector3 inPos) { for (int i = 0; i < list.size; ++i) { // 当前的UI Camera UICamera cam = list.buffer[i]; // Skip inactive scripts if (!cam.enabled || !NGUITools.GetActive(cam.gameObject)) continue; // Convert to view space currentCamera = cam.cachedCamera; // 将屏幕的位置信息,转换成ViewPort的信息,viewport的空间在0, 1范围内 Vector3 pos = currentCamera.ScreenToViewportPoint(inPos); if (float.IsNaN(pos.x) || float.IsNaN(pos.y)) continue; // If it's outside the camera's viewport, do nothing if (pos.x < 0f || pos.x > 1f || pos.y < 0f || pos.y > 1f) continue; // Cast a ray into the screen Ray ray = currentCamera.ScreenPointToRay(inPos); // Raycast into the screen int mask = currentCamera.cullingMask & (int)cam.eventReceiverMask; float dist = (cam.rangeDistance > 0f) ? cam.rangeDistance : currentCamera.farClipPlane - currentCamera.nearClipPlane; if (cam.eventType == EventType.World_3D) { ............ } else if (cam.eventType == EventType.UI_3D) { // 获取当前的所有的RaycastHit的列表,所以在此,需要特别说明的是,任何的NGUI的控件都需要加上BoxColider物体。 disk和camera的距离,mask和层 RaycastHit[] hits = Physics.RaycastAll(ray, dist, mask); if (hits.Length > 1) { // 下面所有的代码获取了点击到的物体的列表, 放入到全局的静态变量mhits中 // 基本的结构单元为: // struct DepthEntry //{ // public int depth; // Gameobject 的深度信息 // public RaycastHit hit; // RaycastHit信息,可以查看具体的unity文档 // public Vector3 point; // hit物体的世界空间的地址 // public GameObject go; // hit 物体 //} for (int b = 0; b < hits.Length; ++b) { GameObject go = hits[b].collider.gameObject; UIWidget w = go.GetComponent<UIWidget>(); if (w != null) { if (!w.isVisible) continue; if (w.hitCheck != null && !w.hitCheck(hits[b].point)) continue; } else { UIRect rect = NGUITools.FindInParents<UIRect>(go); if (rect != null && rect.finalAlpha < 0.001f) continue; } mHit.depth = NGUITools.CalculateRaycastDepth(go); if (mHit.depth != int.MaxValue) { mHit.hit = hits[b]; mHit.point = hits[b].point; mHit.go = hits[b].collider.gameObject; mHits.Add(mHit); } } // 按照深度升序排序, mHits.Sort(delegate(DepthEntry r1, DepthEntry r2) { return r2.depth.CompareTo(r1.depth); }); // 找到depth最高的可视的物体为当前的接触的物体 for (int b = 0; b < mHits.size; ++b) {#if UNITY_FLASH if (IsVisible(mHits.buffer[b]))#else if (IsVisible(ref mHits.buffer[b]))#endif { lastHit = mHits[b].hit; hoveredObject = mHits[b].go; lastWorldPosition = mHits[b].point; mHits.Clear(); return true; } } mHits.Clear(); } else if (hits.Length == 1) { ............ } } else if (cam.eventType == EventType.World_2D) { .............. } else if (cam.eventType == EventType.UI_2D) { .............. } } return false; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- ControlScheme 记录当前的输入的类型
public enum ControlScheme { Mouse, // 鼠标事件 Touch, // 触摸事件 Controller, // 控制器输入 }
- 1
- 2
- 3
- 4
- 5
- 6
- MouseOrTouch 下面是记录鼠标事件和触摸事件的结构体,个人认为实现的不大合理,把鼠标和触摸需要的信息混合在一起了, NGUI在实现中也把touch,mouse事件合并进行了处理。
/// <summary> /// Ambiguous mouse, touch, or controller event. /// </summary> public class MouseOrTouch { public Vector2 pos; // Current position of the mouse or touch event public Vector2 lastPos; // Previous 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 last; // Last object under the touch or mouse public GameObject current; // Current game object under the touch or mouse public GameObject pressed; // Last game object to receive OnPress public GameObject dragged; // Game object that's being dragged public float pressTime = 0f; // When the touch event started 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; /// <summary> /// Delta time since the touch operation started. /// </summary> public float deltaTime { get { return touchBegan ? RealTime.time - pressTime : 0f; } } /// <summary> /// Returns whether this touch is currently over a UI element. /// </summary> public bool isOverUI { get { return current != null && current != fallThrough && NGUITools.FindInParents<UIRoot>(current) != null; } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 处理输入的事件
// 处理鼠标按下或者触摸按下 void ProcessPress (bool pressed, float click, float drag) { // Send out the press message if (pressed) { if (mTooltip != null) ShowTooltip(false); currentTouch.pressStarted = true; // 全局的OnPress的事件通知,前面的物体被释放 if (onPress != null && currentTouch.pressed) onPress(currentTouch.pressed, false); // OnPress 的事件通知, false,前面的物体 Notify(currentTouch.pressed, "OnPress", false); currentTouch.pressed = currentTouch.current; currentTouch.dragged = currentTouch.current; currentTouch.clickNotification = ClickNotification.BasedOnDelta; currentTouch.totalDelta = Vector2.zero; currentTouch.dragStarted = false; // 全局的OnPress的事件通知,当前的物体被按下 if (onPress != null && currentTouch.pressed) onPress(currentTouch.pressed, true); // OnPress 的事件通知, true,当前的物体 Notify(currentTouch.pressed, "OnPress", true); // Update the selection if (currentTouch.pressed != mCurrentSelection) { if (mTooltip != null) ShowTooltip(false); currentScheme = ControlScheme.Touch; selectedObject = currentTouch.pressed; } } // 此处就是处理Drag的各项事务, 注意条件 else if (currentTouch.pressed != null && (currentTouch.delta.sqrMagnitude != 0f || currentTouch.current != currentTouch.last)) { // Keep track of the total movement currentTouch.totalDelta += currentTouch.delta; float mag = currentTouch.totalDelta.sqrMagnitude; bool justStarted = false; // If the drag process hasn't started yet but we've already moved off the object, start it immediately if (!currentTouch.dragStarted && currentTouch.last != currentTouch.current) { currentTouch.dragStarted = true; currentTouch.delta = currentTouch.totalDelta; // OnDragOver is sent for consistency, so that OnDragOut is always preceded by OnDragOver isDragging = true; // 通知OnDragStart 事件到全局函数和现在的物体 if (onDragStart != null) onDragStart(currentTouch.dragged); Notify(currentTouch.dragged, "OnDragStart", null); // 通知OnDragOver 事件到全局函数和上次Drag的物体 if (onDragOver != null) onDragOver(currentTouch.last, currentTouch.dragged); Notify(currentTouch.last, "OnDragOver", currentTouch.dragged); isDragging = false; } else if (!currentTouch.dragStarted && drag < mag) { // If the drag event has not yet started, see if we've dragged the touch far enough to start it justStarted = true; currentTouch.dragStarted = true; currentTouch.delta = currentTouch.totalDelta; } // If we're dragging the touch, send out drag events // 判断DragStarted 发送相关的事件,如 if (currentTouch.dragStarted) { if (mTooltip != null) ShowTooltip(false); isDragging = true; bool isDisabled = (currentTouch.clickNotification == ClickNotification.None); if (justStarted) { if (onDragStart != null) onDragStart(currentTouch.dragged); Notify(currentTouch.dragged, "OnDragStart", null); if (onDragOver != null) onDragOver(currentTouch.last, currentTouch.dragged); Notify(currentTouch.current, "OnDragOver", currentTouch.dragged); } else if (currentTouch.last != currentTouch.current) { if (onDragStart != null) onDragStart(currentTouch.dragged); Notify(currentTouch.last, "OnDragOut", currentTouch.dragged); if (onDragOver != null) onDragOver(currentTouch.last, currentTouch.dragged); Notify(currentTouch.current, "OnDragOver", currentTouch.dragged); } if (onDrag != null) onDrag(currentTouch.dragged, currentTouch.delta); Notify(currentTouch.dragged, "OnDrag", currentTouch.delta); currentTouch.last = currentTouch.current; 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; } } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
/// <summary>/// 处理Touch,和Mouse release事件。/// </summary>void ProcessRelease (bool isMouse, float drag){ // Send out the unpress message currentTouch.pressStarted = false; if (mTooltip != null) ShowTooltip(false); if (currentTouch.pressed != null) { // If there was a drag event in progress, make sure OnDragOut gets sent if (currentTouch.dragStarted) { if (onDragOut != null) onDragOut(currentTouch.last, currentTouch.dragged); Notify(currentTouch.last, "OnDragOut", currentTouch.dragged); if (onDragEnd != null) onDragEnd(currentTouch.dragged); Notify(currentTouch.dragged, "OnDragEnd", null); } // Send the notification of a touch ending if (onPress != null) onPress(currentTouch.pressed, false); Notify(currentTouch.pressed, "OnPress", false); // Send a hover message to the object if (isMouse) { if (onHover != null) onHover(currentTouch.current, true); Notify(currentTouch.current, "OnHover", true); } mHover = currentTouch.current; // If the button/touch was released on the same object, consider it a click and select it if (currentTouch.dragged == currentTouch.current || (currentScheme != ControlScheme.Controller && currentTouch.clickNotification != ClickNotification.None && currentTouch.totalDelta.sqrMagnitude < drag)) { if (currentTouch.pressed != mCurrentSelection) { mNextSelection = null; mCurrentSelection = currentTouch.pressed; if (onSelect != null) onSelect(currentTouch.pressed, true); Notify(currentTouch.pressed, "OnSelect", true); } else { mNextSelection = null; mCurrentSelection = currentTouch.pressed; } // If the touch should consider clicks, send out an OnClick notification if (currentTouch.clickNotification != ClickNotification.None && currentTouch.pressed == currentTouch.current) { float time = RealTime.time; if (onClick != null) onClick(currentTouch.pressed); Notify(currentTouch.pressed, "OnClick", null); if (currentTouch.clickTime + 0.35f > time) { if (onDoubleClick != null) onDoubleClick(currentTouch.pressed); Notify(currentTouch.pressed, "OnDoubleClick", null); } currentTouch.clickTime = time; } } else if (currentTouch.dragStarted) // The button/touch was released on a different object { // Send a drop notification (for drag & drop) if (onDrop != null) onDrop(currentTouch.current, currentTouch.dragged); Notify(currentTouch.current, "OnDrop", currentTouch.dragged); } } currentTouch.dragStarted = false; currentTouch.pressed = null; currentTouch.dragged = null;}
- Update 函数,其处理UI 事件,并且发送给具体的物体和回调函数。
void Update () { // Only the first UI layer should be processing events#if UNITY_EDITOR if (!Application.isPlaying || !handlesEvents) return;#else if (!handlesEvents) return;#endif current = this; // Process touch events first // 处理触摸和鼠标事件,可以review源代码,都是复用了ProcessPress和ProcessRelease // 函数 if (useTouch) ProcessTouches (); else if (useMouse) ProcessMouse(); .................. }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
总结: 所有整个NGUI的事件处理通知都是由UICamera的update函数,分析当前帧的用户输入,然后发送时间给NGUI的UI控件,如UIButton,UIScrollView等。
0 0
- NGUI的事件通知架构和源码剖析--UICamera
- NGUI的事件通知架构和源码剖析
- NGUI的事件通知架构和源码剖析
- NGUI的核心组件UICamera
- NGUI之UICamera控制触摸,鼠标事件
- NGUI之UICamera控制触摸,鼠标事件
- 【NGUI源码剖析】NGUI的drawcall
- UICamera的触摸事件
- NGUI -- UICamera
- NGUI--UICamera
- NGUI:UICamera
- NGUI中UICamera的EventType详解
- Unity3D:NGUI 深入剖析NGUI的游戏UI架构
- NGUI -- UICamera(监听输入事件,鼠标,键盘等)
- 【NGUI源码剖析】深入理解NGUI的drawcall
- NGUI UICamera属性介绍
- 【Unity3D】【NGUI】UICamera
- 【Unity3D】【NGUI】UICamera
- Linux、ubuntu将文件移动到指定文件夹
- HDU5965 扫雷 —— dp递推
- java动态生成pdf含表格table和 合并两个pdf文件功能
- java之运算符
- linux下用subversionedge创建版本库时遇到的问题
- NGUI的事件通知架构和源码剖析--UICamera
- 第二场选拔赛
- LintCode | 111. 爬楼梯
- IT领域关键词
- 解决电脑不能删除的文件
- ife系列之网页页面“小三角”的实现方式
- 新三板是什么?
- xx-net+Chrome傻瓜式教程
- 洛谷 2936_[USACO09JAN]全流Total Flow_网络流