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
- 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的上述事件处理函数。 如果自己处理了,可能同一个用户输入会响应两次。
下面就简单介绍一下关键的函数或者结构体
static public void Notify (GameObject go, string funcName, object obj)
{
if (mNotifying) return;
mNotifying = true;
if (NGUITools.GetActive(go))
{
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
- 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 帮助函数,如何从一个屏幕上的位置信息,找到点击,触摸,滑过的物体
static public bool Raycast (Vector3 inPos)
{
for (int i = 0; i < list.size; ++i)
{
UICamera cam = list.buffer[i];
if (!cam.enabled || !NGUITools.GetActive(cam.gameObject)) continue;
currentCamera = cam.cachedCamera;
Vector3 pos = currentCamera.ScreenToViewportPoint(inPos);
if (float.IsNaN(pos.x) || float.IsNaN(pos.y)) continue;
if (pos.x < 0f || pos.x > 1f || pos.y < 0f || pos.y > 1f) continue;
Ray ray = currentCamera.ScreenPointToRay(inPos);
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[] hits = Physics.RaycastAll(ray, dist, mask);
if (hits.Length > 1)
{
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); });
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
- 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
public enum ControlScheme
{
Mouse,
Touch,
Controller,
}
- MouseOrTouch 下面是记录鼠标事件和触摸事件的结构体,个人认为实现的不大合理,把鼠标和触摸需要的信息混合在一起了, NGUI在实现中也把touch,mouse事件合并进行了处理。
public class MouseOrTouch
{
public Vector2 pos;
public Vector2 lastPos;
public Vector2 delta;
public Vector2 totalDelta;
public Camera pressedCam;
public GameObject last;
public GameObject current;
public GameObject pressed;
public GameObject dragged;
public float pressTime = 0f;
public float clickTime = 0f;
public ClickNotification clickNotification = ClickNotification.Always;
public bool touchBegan = true;
public bool pressStarted = false;
public bool dragStarted = false;
public float deltaTime { get { return touchBegan ? RealTime.time - pressTime : 0f; } }
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
- 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)
{
if (pressed)
{
if (mTooltip != null) ShowTooltip(false);
currentTouch.pressStarted = true;
if (onPress != null && currentTouch.pressed)
onPress(currentTouch.pressed, 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;
if (onPress != null && currentTouch.pressed)
onPress(currentTouch.pressed, true);
Notify(currentTouch.pressed, "OnPress", true);
if (currentTouch.pressed != mCurrentSelection)
{
if (mTooltip != null) ShowTooltip(false);
currentScheme = ControlScheme.Touch;
selectedObject = currentTouch.pressed;
}
}
else if (currentTouch.pressed != null && (currentTouch.delta.sqrMagnitude != 0f || currentTouch.current != currentTouch.last))
{
currentTouch.totalDelta += currentTouch.delta;
float mag = currentTouch.totalDelta.sqrMagnitude;
bool justStarted = false;
if (!currentTouch.dragStarted && currentTouch.last != currentTouch.current)
{
currentTouch.dragStarted = true;
currentTouch.delta = currentTouch.totalDelta;
isDragging = true;
if (onDragStart != null) onDragStart(currentTouch.dragged);
Notify(currentTouch.dragged, "OnDragStart", null);
if (onDragOver != null) onDragOver(currentTouch.last, currentTouch.dragged);
Notify(currentTouch.last, "OnDragOver", currentTouch.dragged);
isDragging = false;
}
else if (!currentTouch.dragStarted && drag < mag)
{
justStarted = true;
currentTouch.dragStarted = true;
currentTouch.delta = currentTouch.totalDelta;
}
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)
{
currentTouch.clickNotification = ClickNotification.None;
}
else if (currentTouch.clickNotification == ClickNotification.BasedOnDelta && click < mag)
{
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
- 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 ()
{
#if UNITY_EDITOR
if (!Application.isPlaying || !handlesEvents) return;
#else
if (!handlesEvents) return;
#endif
current = this;
if (useTouch) ProcessTouches ();
else if (useMouse) ProcessMouse();
..................
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
总结: 所有整个NGUI的事件处理通知都是由UICamera的update函数,分析当前帧的用户输入,然后发送时间给NGUI的UI控件,如UIButton,UIScrollView等。