version 2.5.0
This commit is contained in:
@@ -0,0 +1,595 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace VIVE.OpenXR.Raycast
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
[RequireComponent(typeof(Camera))]
|
||||
public class RaycastImpl : BaseRaycaster
|
||||
{
|
||||
#region Log
|
||||
StringBuilder m_sb = null;
|
||||
protected StringBuilder sb {
|
||||
get {
|
||||
if (m_sb == null) { m_sb = new StringBuilder(); }
|
||||
return m_sb;
|
||||
}
|
||||
}
|
||||
const string LOG_TAG = "VIVE.OpenXR.Raycast.RaycastImpl";
|
||||
void DEBUG(StringBuilder msg) { Debug.LogFormat("{0} {1}", LOG_TAG, msg); }
|
||||
#endregion
|
||||
|
||||
#region Inspector
|
||||
[SerializeField]
|
||||
private bool m_IgnoreReversedGraphics = false;
|
||||
public bool IgnoreReversedGraphics { get { return m_IgnoreReversedGraphics; } set { m_IgnoreReversedGraphics = value; } }
|
||||
[SerializeField]
|
||||
private float m_PhysicsCastDistance = 100;
|
||||
public float PhysicsCastDistance { get { return m_PhysicsCastDistance; } set { m_PhysicsCastDistance = value; } }
|
||||
[SerializeField]
|
||||
private LayerMask m_PhysicsEventMask = ~0;
|
||||
public LayerMask PhysicsEventMask { get { return m_PhysicsEventMask; } set { m_PhysicsEventMask = value; } }
|
||||
#endregion
|
||||
|
||||
private Camera m_Camera = null;
|
||||
public override Camera eventCamera { get { return m_Camera; } }
|
||||
|
||||
#region MonoBehaviour overrides
|
||||
protected override void OnEnable()
|
||||
{
|
||||
sb.Clear().Append("OnEnable()"); DEBUG(sb);
|
||||
base.OnEnable();
|
||||
|
||||
/// 1. Set up the event camera.
|
||||
m_Camera = GetComponent<Camera>();
|
||||
m_Camera.stereoTargetEye = StereoTargetEyeMask.None;
|
||||
m_Camera.enabled = false;
|
||||
|
||||
/// 2. Set up the EventSystem.
|
||||
if (EventSystem.current == null)
|
||||
{
|
||||
var eventSystemObject = new GameObject("EventSystem");
|
||||
eventSystemObject.AddComponent<EventSystem>();
|
||||
}
|
||||
}
|
||||
protected override void OnDisable()
|
||||
{
|
||||
sb.Clear().Append("OnDisable()"); DEBUG(sb);
|
||||
base.OnDisable();
|
||||
}
|
||||
|
||||
int printFrame = 0;
|
||||
protected bool printIntervalLog = false;
|
||||
|
||||
protected bool m_Interactable = true;
|
||||
protected virtual void Update()
|
||||
{
|
||||
printFrame++;
|
||||
printFrame %= 300;
|
||||
printIntervalLog = (printFrame == 0);
|
||||
|
||||
if (!m_Interactable) return;
|
||||
|
||||
/// Use the event camera and EventSystem to reset PointerEventData.
|
||||
ResetEventData();
|
||||
|
||||
/// Update the raycast results
|
||||
resultAppendList.Clear();
|
||||
Raycast(pointerData, resultAppendList);
|
||||
|
||||
pointerData.pointerCurrentRaycast = currentRaycastResult;
|
||||
|
||||
/// Send events
|
||||
HandleRaycastEvent();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Raycast Result Handling
|
||||
static readonly Comparison<RaycastResult> rrComparator = RaycastResultComparator;
|
||||
private RaycastResult GetFirstRaycastResult(List<RaycastResult> results)
|
||||
{
|
||||
RaycastResult rr = default;
|
||||
|
||||
results.Sort(rrComparator);
|
||||
for (int i = 0; i < results.Count; i++)
|
||||
{
|
||||
if (results[i].isValid)
|
||||
{
|
||||
rr = results[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rr;
|
||||
}
|
||||
private static int RaycastResultComparator(RaycastResult lhs, RaycastResult rhs)
|
||||
{
|
||||
if (lhs.module != rhs.module)
|
||||
{
|
||||
if (lhs.module.eventCamera != null && rhs.module.eventCamera != null && lhs.module.eventCamera.depth != rhs.module.eventCamera.depth)
|
||||
{
|
||||
// need to reverse the standard compareTo
|
||||
if (lhs.module.eventCamera.depth < rhs.module.eventCamera.depth) { return 1; }
|
||||
if (lhs.module.eventCamera.depth == rhs.module.eventCamera.depth) { return 0; }
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lhs.module.sortOrderPriority != rhs.module.sortOrderPriority)
|
||||
{
|
||||
return rhs.module.sortOrderPriority.CompareTo(lhs.module.sortOrderPriority);
|
||||
}
|
||||
|
||||
if (lhs.module.renderOrderPriority != rhs.module.renderOrderPriority)
|
||||
{
|
||||
return rhs.module.renderOrderPriority.CompareTo(lhs.module.renderOrderPriority);
|
||||
}
|
||||
}
|
||||
|
||||
if (lhs.sortingLayer != rhs.sortingLayer)
|
||||
{
|
||||
// Uses the layer value to properly compare the relative order of the layers.
|
||||
var rid = SortingLayer.GetLayerValueFromID(rhs.sortingLayer);
|
||||
var lid = SortingLayer.GetLayerValueFromID(lhs.sortingLayer);
|
||||
return rid.CompareTo(lid);
|
||||
}
|
||||
|
||||
if (lhs.sortingOrder != rhs.sortingOrder)
|
||||
{
|
||||
return rhs.sortingOrder.CompareTo(lhs.sortingOrder);
|
||||
}
|
||||
|
||||
if (!Mathf.Approximately(lhs.distance, rhs.distance))
|
||||
{
|
||||
return lhs.distance.CompareTo(rhs.distance);
|
||||
}
|
||||
|
||||
if (lhs.depth != rhs.depth)
|
||||
{
|
||||
return rhs.depth.CompareTo(lhs.depth);
|
||||
}
|
||||
|
||||
return lhs.index.CompareTo(rhs.index);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#if UNITY_EDITOR
|
||||
bool drawDebugLine = false;
|
||||
#endif
|
||||
|
||||
#region Raycast
|
||||
protected virtual bool UseEyeData(out Vector3 direction)
|
||||
{
|
||||
direction = Vector3.forward;
|
||||
return false;
|
||||
}
|
||||
protected PointerEventData pointerData = null;
|
||||
protected Vector3 pointerLocalOffset = Vector3.forward;
|
||||
private Vector3 physicsWorldPosition = Vector3.zero;
|
||||
private Vector2 graphicScreenPosition = Vector2.zero;
|
||||
private void UpdatePointerDataPosition()
|
||||
{
|
||||
/// 1. Calculate the pointer offset in "local" space.
|
||||
pointerLocalOffset = Vector3.forward;
|
||||
if (UseEyeData(out Vector3 direction))
|
||||
{
|
||||
pointerLocalOffset = direction;
|
||||
|
||||
// Revise the offset from World space to Local space.
|
||||
// OpenXR always uses World space.
|
||||
pointerLocalOffset = Quaternion.Inverse(transform.rotation) * pointerLocalOffset;
|
||||
}
|
||||
|
||||
/// 2. Calculate the pointer position in "world" space.
|
||||
Vector3 rotated_offset = transform.rotation * pointerLocalOffset;
|
||||
physicsWorldPosition = transform.position + rotated_offset;
|
||||
graphicScreenPosition = m_Camera.WorldToScreenPoint(physicsWorldPosition);
|
||||
// The graphicScreenPosition.x should be equivalent to (0.5f * Screen.width);
|
||||
// The graphicScreenPosition.y should be equivalent to (0.5f * Screen.height);
|
||||
}
|
||||
private void ResetEventData()
|
||||
{
|
||||
if (pointerData == null) { pointerData = new RaycastEventData(EventSystem.current, gameObject); }
|
||||
|
||||
UpdatePointerDataPosition();
|
||||
pointerData.position = graphicScreenPosition;
|
||||
}
|
||||
List<RaycastResult> resultAppendList = new List<RaycastResult>();
|
||||
private RaycastResult currentRaycastResult = default;
|
||||
protected GameObject raycastObject = null;
|
||||
protected List<GameObject> s_raycastObjects = new List<GameObject>();
|
||||
protected GameObject raycastObjectEx = null;
|
||||
protected List<GameObject> s_raycastObjectsEx = new List<GameObject>();
|
||||
/**
|
||||
* Call to
|
||||
* GraphicRaycast(Canvas canvas, Camera eventCamera, Vector2 screenPosition, List<RaycastResult> resultAppendList)
|
||||
* PhysicsRaycast(Ray ray, Camera eventCamera, List<RaycastResult> resultAppendList)
|
||||
**/
|
||||
public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
|
||||
{
|
||||
// --------------- Previous Results ---------------
|
||||
raycastObjectEx = raycastObject;
|
||||
s_raycastObjects.Clear();
|
||||
|
||||
// --------------- Graphic Raycast ---------------
|
||||
Canvas[] canvases = CanvasProvider.GetTargetCanvas();
|
||||
if (canvases.Length <= 0) { canvases = FindObjectsOfType<Canvas>(); } // note: GC.Alloc
|
||||
for (int i = 0; i < canvases.Length; i++)
|
||||
{
|
||||
GraphicRaycast(canvases[i], m_Camera, eventData.position, resultAppendList);
|
||||
}
|
||||
|
||||
// --------------- Physics Raycast ---------------
|
||||
Ray ray = new Ray(transform.position, (physicsWorldPosition - transform.position));
|
||||
PhysicsRaycast(ray, m_Camera, resultAppendList);
|
||||
|
||||
currentRaycastResult = GetFirstRaycastResult(resultAppendList);
|
||||
|
||||
// --------------- Current Results ---------------
|
||||
raycastObject = currentRaycastResult.gameObject;
|
||||
|
||||
GameObject raycastTarget = currentRaycastResult.gameObject;
|
||||
while (raycastTarget != null)
|
||||
{
|
||||
s_raycastObjects.Add(raycastTarget);
|
||||
raycastTarget = (raycastTarget.transform.parent != null ? raycastTarget.transform.parent.gameObject : null);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (drawDebugLine)
|
||||
{
|
||||
Vector3 end = transform.position + (transform.forward * 100);
|
||||
Debug.DrawLine(transform.position, end, Color.red, 1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Ray ray = new Ray();
|
||||
protected virtual void GraphicRaycast(Canvas canvas, Camera eventCamera, Vector2 screenPosition, List<RaycastResult> resultAppendList)
|
||||
{
|
||||
if (canvas == null)
|
||||
return;
|
||||
|
||||
IList<Graphic> foundGraphics = GraphicRegistry.GetGraphicsForCanvas(canvas);
|
||||
if (foundGraphics == null || foundGraphics.Count == 0)
|
||||
return;
|
||||
|
||||
int displayIndex = 0;
|
||||
var currentEventCamera = eventCamera; // Property can call Camera.main, so cache the reference
|
||||
|
||||
if (canvas.renderMode == RenderMode.ScreenSpaceOverlay || currentEventCamera == null)
|
||||
displayIndex = canvas.targetDisplay;
|
||||
else
|
||||
displayIndex = currentEventCamera.targetDisplay;
|
||||
|
||||
if (currentEventCamera != null)
|
||||
ray = currentEventCamera.ScreenPointToRay(screenPosition);
|
||||
|
||||
// Necessary for the event system
|
||||
for (int i = 0; i < foundGraphics.Count; ++i)
|
||||
{
|
||||
Graphic graphic = foundGraphics[i];
|
||||
|
||||
// -1 means it hasn't been processed by the canvas, which means it isn't actually drawn
|
||||
if (!graphic.raycastTarget || graphic.canvasRenderer.cull || graphic.depth == -1) { continue; }
|
||||
|
||||
if (!RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, screenPosition, currentEventCamera)) { continue; }
|
||||
|
||||
if (currentEventCamera != null && currentEventCamera.WorldToScreenPoint(graphic.rectTransform.position).z > currentEventCamera.farClipPlane) { continue; }
|
||||
|
||||
if (graphic.Raycast(screenPosition, currentEventCamera))
|
||||
{
|
||||
var go = graphic.gameObject;
|
||||
bool appendGraphic = true;
|
||||
|
||||
if (m_IgnoreReversedGraphics)
|
||||
{
|
||||
if (currentEventCamera == null)
|
||||
{
|
||||
// If we dont have a camera we know that we should always be facing forward
|
||||
var dir = go.transform.rotation * Vector3.forward;
|
||||
appendGraphic = Vector3.Dot(Vector3.forward, dir) > 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we have a camera compare the direction against the cameras forward.
|
||||
var cameraForward = currentEventCamera.transform.rotation * Vector3.forward * currentEventCamera.nearClipPlane;
|
||||
appendGraphic = Vector3.Dot(go.transform.position - currentEventCamera.transform.position - cameraForward, go.transform.forward) >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (appendGraphic)
|
||||
{
|
||||
float distance = 0;
|
||||
Transform trans = go.transform;
|
||||
Vector3 transForward = trans.forward;
|
||||
|
||||
if (currentEventCamera == null || canvas.renderMode == RenderMode.ScreenSpaceOverlay)
|
||||
distance = 0;
|
||||
else
|
||||
{
|
||||
// http://geomalgorithms.com/a06-_intersect-2.html
|
||||
distance = (Vector3.Dot(transForward, trans.position - ray.origin) / Vector3.Dot(transForward, ray.direction));
|
||||
|
||||
// Check to see if the go is behind the camera.
|
||||
if (distance < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
resultAppendList.Add(new RaycastResult
|
||||
{
|
||||
gameObject = go,
|
||||
module = this,
|
||||
distance = distance,
|
||||
screenPosition = screenPosition,
|
||||
displayIndex = displayIndex,
|
||||
index = resultAppendList.Count,
|
||||
depth = graphic.depth,
|
||||
sortingLayer = canvas.sortingLayerID,
|
||||
sortingOrder = canvas.sortingOrder,
|
||||
worldPosition = ray.origin + ray.direction * distance,
|
||||
worldNormal = -transForward
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 hitScreenPos = Vector3.zero;
|
||||
Vector2 hitScreenPos2D = Vector2.zero;
|
||||
static readonly RaycastHit[] hits = new RaycastHit[255];
|
||||
protected virtual void PhysicsRaycast(Ray ray, Camera eventCamera, List<RaycastResult> resultAppendList)
|
||||
{
|
||||
var hitCount = Physics.RaycastNonAlloc(ray, hits, m_PhysicsCastDistance, m_PhysicsEventMask);
|
||||
|
||||
for (int i = 0; i < hitCount; ++i)
|
||||
{
|
||||
hitScreenPos = eventCamera.WorldToScreenPoint(hits[i].point);
|
||||
hitScreenPos2D.x = hitScreenPos.x;
|
||||
hitScreenPos2D.y = hitScreenPos.y;
|
||||
|
||||
resultAppendList.Add(new RaycastResult
|
||||
{
|
||||
gameObject = hits[i].collider.gameObject,
|
||||
module = this,
|
||||
distance = hits[i].distance,
|
||||
worldPosition = hits[i].point,
|
||||
worldNormal = hits[i].normal,
|
||||
screenPosition = hitScreenPos2D,
|
||||
index = resultAppendList.Count,
|
||||
sortingLayer = 0,
|
||||
sortingOrder = 0
|
||||
});
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Event
|
||||
private void CopyList(List<GameObject> src, List<GameObject> dst)
|
||||
{
|
||||
dst.Clear();
|
||||
for (int i = 0; i < src.Count; i++)
|
||||
dst.Add(src[i]);
|
||||
}
|
||||
|
||||
private void ExitEnterHandler(ref List<GameObject> enterObjects, ref List<GameObject> exitObjects)
|
||||
{
|
||||
if (exitObjects.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < exitObjects.Count; i++)
|
||||
{
|
||||
if (exitObjects[i] != null && !enterObjects.Contains(exitObjects[i]))
|
||||
{
|
||||
ExecuteEvents.Execute(exitObjects[i], pointerData, ExecuteEvents.pointerExitHandler);
|
||||
sb.Clear().Append("ExitEnterHandler() Exit: ").Append(exitObjects[i].name); DEBUG(sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (enterObjects.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < enterObjects.Count; i++)
|
||||
{
|
||||
if (enterObjects[i] != null && !exitObjects.Contains(enterObjects[i]))
|
||||
{
|
||||
ExecuteEvents.Execute(enterObjects[i], pointerData, ExecuteEvents.pointerEnterHandler);
|
||||
sb.Clear().Append("ExitEnterHandler() Enter: ").Append(enterObjects[i].name).Append(", camera: ").Append(pointerData.enterEventCamera); DEBUG(sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CopyList(enterObjects, exitObjects);
|
||||
}
|
||||
private void HoverHandler()
|
||||
{
|
||||
if (raycastObject != null && (raycastObject == raycastObjectEx))
|
||||
{
|
||||
if (printIntervalLog) { sb.Clear().Append("HoverHandler() Hover: ").Append(raycastObject.name); DEBUG(sb); }
|
||||
ExecuteEvents.ExecuteHierarchy(raycastObject, pointerData, RaycastEvents.pointerHoverHandler);
|
||||
}
|
||||
}
|
||||
private void DownHandler()
|
||||
{
|
||||
sb.Clear().Append("DownHandler()");DEBUG(sb);
|
||||
if (raycastObject == null) { return; }
|
||||
|
||||
pointerData.pressPosition = pointerData.position;
|
||||
pointerData.pointerPressRaycast = pointerData.pointerCurrentRaycast;
|
||||
pointerData.pointerPress =
|
||||
ExecuteEvents.ExecuteHierarchy(raycastObject, pointerData, ExecuteEvents.pointerDownHandler)
|
||||
?? ExecuteEvents.GetEventHandler<IPointerClickHandler>(raycastObject);
|
||||
|
||||
sb.Clear().Append("DownHandler() Down: ").Append(pointerData.pointerPress).Append(", raycastObject: ").Append(raycastObject.name); DEBUG(sb);
|
||||
|
||||
// If Drag Handler exists, send initializePotentialDrag event.
|
||||
pointerData.pointerDrag = ExecuteEvents.GetEventHandler<IDragHandler>(raycastObject);
|
||||
if (pointerData.pointerDrag != null)
|
||||
{
|
||||
sb.Clear().Append("DownHandler() Send initializePotentialDrag to ").Append(pointerData.pointerDrag.name).Append(", current GameObject is ").Append(raycastObject.name); DEBUG(sb);
|
||||
ExecuteEvents.Execute(pointerData.pointerDrag, pointerData, ExecuteEvents.initializePotentialDrag);
|
||||
}
|
||||
|
||||
// press happened (even not handled) object.
|
||||
pointerData.rawPointerPress = raycastObject;
|
||||
// allow to send Pointer Click event
|
||||
pointerData.eligibleForClick = true;
|
||||
// reset the screen position of press, can be used to estimate move distance
|
||||
pointerData.delta = Vector2.zero;
|
||||
// current Down, reset drag state
|
||||
pointerData.dragging = false;
|
||||
pointerData.useDragThreshold = true;
|
||||
// record the count of Pointer Click should be processed, clean when Click event is sent.
|
||||
pointerData.clickCount = 1;
|
||||
// set clickTime to current time of Pointer Down instead of Pointer Click.
|
||||
// since Down & Up event should not be sent too closely. (< kClickInterval)
|
||||
pointerData.clickTime = Time.unscaledTime;
|
||||
}
|
||||
private void UpHandler()
|
||||
{
|
||||
if (!pointerData.eligibleForClick && !pointerData.dragging)
|
||||
{
|
||||
// 1. no pending click
|
||||
// 2. no dragging
|
||||
// Mean user has finished all actions and do NOTHING in current frame.
|
||||
return;
|
||||
}
|
||||
|
||||
// raycastObject may be different with pointerData.pointerDrag so we don't check null
|
||||
|
||||
if (pointerData.pointerPress != null)
|
||||
{
|
||||
// In the frame of button is pressed -> unpressed, send Pointer Up
|
||||
sb.Clear().Append("UpHandler() Send Pointer Up to ").Append(pointerData.pointerPress.name); DEBUG(sb);
|
||||
ExecuteEvents.Execute(pointerData.pointerPress, pointerData, ExecuteEvents.pointerUpHandler);
|
||||
}
|
||||
|
||||
if (pointerData.eligibleForClick)
|
||||
{
|
||||
GameObject objectToClick = ExecuteEvents.GetEventHandler<IPointerClickHandler>(raycastObject);
|
||||
if (objectToClick != null)
|
||||
{
|
||||
if (objectToClick == pointerData.pointerPress)
|
||||
{
|
||||
// In the frame of button from being pressed to unpressed, send Pointer Click if Click is pending.
|
||||
sb.Clear().Append("UpHandler() Send Pointer Click to ").Append(pointerData.pointerPress.name); DEBUG(sb);
|
||||
ExecuteEvents.Execute(pointerData.pointerPress, pointerData, ExecuteEvents.pointerClickHandler);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Clear().Append("UpHandler() pointer down object ").Append(pointerData.pointerPress).Append(" is different with click object ").Append(objectToClick.name); DEBUG(sb);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pointerData.dragging)
|
||||
{
|
||||
GameObject _pointerDrop = ExecuteEvents.GetEventHandler<IDropHandler>(raycastObject);
|
||||
if (_pointerDrop == pointerData.pointerDrag)
|
||||
{
|
||||
// In next frame of button from being pressed to unpressed, send Drop and EndDrag if dragging.
|
||||
sb.Clear().Append("UpHandler() Send Pointer Drop to ").Append(pointerData.pointerDrag); DEBUG(sb);
|
||||
ExecuteEvents.Execute(pointerData.pointerDrag, pointerData, ExecuteEvents.dropHandler);
|
||||
}
|
||||
sb.Clear().Append("UpHandler() Send Pointer endDrag to ").Append(pointerData.pointerDrag); DEBUG(sb);
|
||||
ExecuteEvents.Execute(pointerData.pointerDrag, pointerData, ExecuteEvents.endDragHandler);
|
||||
|
||||
pointerData.dragging = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// initializePotentialDrag was sent when IDragHandler exists.
|
||||
pointerData.pointerDrag = null;
|
||||
// Down of pending Click object.
|
||||
pointerData.pointerPress = null;
|
||||
// press happened (even not handled) object.
|
||||
pointerData.rawPointerPress = null;
|
||||
// clear pending state.
|
||||
pointerData.eligibleForClick = false;
|
||||
// Click is processed, clearcount.
|
||||
pointerData.clickCount = 0;
|
||||
// Up is processed thus clear the time limitation of Down event.
|
||||
pointerData.clickTime = 0;
|
||||
}
|
||||
|
||||
// After selecting an object over this duration, the drag action will be taken.
|
||||
const float kTimeToDrag = 0.2f;
|
||||
private void DragHandler()
|
||||
{
|
||||
if (Time.unscaledTime - pointerData.clickTime < kTimeToDrag) { return; }
|
||||
if (pointerData.pointerDrag == null) { return; }
|
||||
|
||||
if (!pointerData.dragging)
|
||||
{
|
||||
sb.Clear().Append("DragHandler() Send BeginDrag to ").Append(pointerData.pointerDrag.name); DEBUG(sb);
|
||||
ExecuteEvents.Execute(pointerData.pointerDrag, pointerData, ExecuteEvents.beginDragHandler);
|
||||
pointerData.dragging = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecuteEvents.Execute(pointerData.pointerDrag, pointerData, ExecuteEvents.dragHandler);
|
||||
}
|
||||
}
|
||||
|
||||
private void SubmitHandler()
|
||||
{
|
||||
if (raycastObject == null) { return; }
|
||||
|
||||
sb.Clear().Append("SubmitHandler() Submit: ").Append(raycastObject.name); DEBUG(sb);
|
||||
ExecuteEvents.ExecuteHierarchy(raycastObject, pointerData, ExecuteEvents.submitHandler);
|
||||
}
|
||||
|
||||
// Do NOT allow event DOWN being sent multiple times during kClickInterval
|
||||
// since UI element of Unity needs time to perform transitions.
|
||||
const float kClickInterval = 0.2f;
|
||||
private void HandleRaycastEvent()
|
||||
{
|
||||
ExitEnterHandler(ref s_raycastObjects, ref s_raycastObjectsEx);
|
||||
HoverHandler();
|
||||
|
||||
bool submit = OnSubmit();
|
||||
if (submit)
|
||||
{
|
||||
SubmitHandler();
|
||||
return;
|
||||
}
|
||||
|
||||
bool down = OnDown();
|
||||
bool hold = OnHold();
|
||||
if (!down && hold)
|
||||
{
|
||||
// Hold means to Drag.
|
||||
DragHandler();
|
||||
}
|
||||
else if (Time.unscaledTime - pointerData.clickTime < kClickInterval)
|
||||
{
|
||||
// Delay new events until kClickInterval has passed.
|
||||
}
|
||||
else if (down && !pointerData.eligibleForClick)
|
||||
{
|
||||
// 1. Not Down -> Down
|
||||
// 2. No pending Click should be procced.
|
||||
DownHandler();
|
||||
}
|
||||
else if (!hold)
|
||||
{
|
||||
// 1. If Down before, send Up event and clear Down state.
|
||||
// 2. If Dragging, send Drop & EndDrag event and clear Dragging state.
|
||||
// 3. If no Down or Dragging state, do NOTHING.
|
||||
UpHandler();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Actions
|
||||
protected virtual bool OnDown() { return false; }
|
||||
protected virtual bool OnHold() { return false; }
|
||||
protected virtual bool OnSubmit() { return false; }
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user