Search Unity

Question VR Occulus integration additive scenes with pointable canvas module, causing crash

Discussion in 'VR' started by DaiPerc, Mar 14, 2023.

  1. DaiPerc

    DaiPerc

    Joined:
    Oct 29, 2021
    Posts:
    6
    I'm working on a project with additive scenes and utilising passthrough for the Quest Pro. I have a main menu scene with a canvas to take you to different scenes. once I press the button to take me to a level (In this level there is another worldspace UI menu with the same canvas layout) The event system is in the inital scene and is carried throughout the experience.

    When i select the button in the menu scene to go to the following menu the game crashes and I'm giving the error:

    MissingReferenceException: The object of type 'Canvas' has been destroyed but you are still trying to access it.
    Your script should either check if it is null or you should not destroy the object.
    Oculus.Interaction.PointableCanvasModule.UpdateRaycasts (Oculus.Interaction.PointableCanvasModule+Pointer pointer, System.Boolean& pressed, System.Boolean& released) (at Assets/Oculus/Interaction/Runtime/Scripts/Unity/PointableCanvasModule.cs:288)
    Oculus.Interaction.PointableCanvasModule.ProcessPointer (Oculus.Interaction.PointableCanvasModule+Pointer pointer, System.Boolean forceRelease) (at Assets/Oculus/Interaction/Runtime/Scripts/Unity/PointableCanvasModule.cs:360)
    Oculus.Interaction.PointableCanvasModule.Process () (at Assets/Oculus/Interaction/Runtime/Scripts/Unity/PointableCanvasModule.cs:344)
    UnityEngine.EventSystems.EventSystem.Update () (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/EventSystem.cs:501)

    This error directs me to the script that accompanies the pointable canvas module attached to the event system. This is The script that comes with Oculus integration and i haven't written it myself. Whats happening is when the menu scene is unloaded the event system which is attached to the initial scene (along with the VR Rig etc) loses it and crashes.

    Does anyone have any idea how alter the code to stop this from happening?

    The scene that is being loaded has the same canvas setup etc.

    Here's the code, any help would be highly appreciated!

    Its sending me to line 288 when i click on the error. Heres the section where i believe it's causing problems.

    Code (CSharp):
    1. protected override void OnEnable()
    2.         {
    3.             base.OnEnable();
    4.             if (_started)
    5.             {
    6.                 _pointerEventCamera = gameObject.AddComponent<Camera>();
    7.                 _pointerEventCamera.nearClipPlane = 0.1f;
    8.  
    9.                 // We do not need this camera to be enabled to serve this module's purposes:
    10.                 // as a dependency for Canvases and for its WorldToScreenPoint functionality
    11.                 _pointerEventCamera.enabled = false;
    12.             }
    13.         }
    14.  
    15.         protected override void OnDisable()
    16.         {
    17.             if (_started)
    18.             {
    19.                 Destroy(_pointerEventCamera);
    20.                 _pointerEventCamera = null;
    21.             }
    22.             base.OnDisable();
    23.         }
    24.  
    25.         // Based On FindFirstRaycast
    26.         protected static RaycastResult FindFirstRaycastWithinCanvas(List<RaycastResult> candidates, Canvas canvas)
    27.         {
    28.             GameObject candidateGameObject;
    29.             Canvas candidateCanvas;
    30.             for (var i = 0; i < candidates.Count; ++i)
    31.             {
    32.                 candidateGameObject = candidates[i].gameObject;
    33.                 if (candidateGameObject == null) continue;
    34.  
    35.                 candidateCanvas = candidateGameObject.GetComponentInParent<Canvas>();
    36.                 if (candidateCanvas == null) continue;
    37.                 if (candidateCanvas.rootCanvas != canvas) continue;
    38.  
    39.                 return candidates[i];
    40.             }
    41.             return new RaycastResult();
    42.         }
    43.  
    44.         private void UpdateRaycasts(Pointer pointer, out bool pressed, out bool released)
    45.         {
    46.             PointerEventData pointerEventData = pointer.PointerEventData;
    47.  
    48.             Vector2 prevPosition = pointerEventData.position;
    49.             Canvas canvas = pointer.Canvas;
    50.             canvas.worldCamera = _pointerEventCamera;
    51.  
    52.             pointerEventData.Reset();
    53.  
    54.             pointer.ReadAndResetPressedReleased(out pressed, out released);
    Here's the full code.

    Evan.

    Code (CSharp):
    1. /*
    2. * Copyright (c) Meta Platforms, Inc. and affiliates.
    3. * All rights reserved.
    4. *
    5. * Licensed under the Oculus SDK License Agreement (the "License");
    6. * you may not use the Oculus SDK except in compliance with the License,
    7. * which is provided at the time of installation or download, or which
    8. * otherwise accompanies this software in either electronic or hard copy form.
    9. *
    10. * You may obtain a copy of the License at
    11. *
    12. * https://developer.oculus.com/licenses/oculussdk/
    13. *
    14. * Unless required by applicable law or agreed to in writing, the Oculus SDK
    15. * distributed under the License is distributed on an "AS IS" BASIS,
    16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    17. * See the License for the specific language governing permissions and
    18. * limitations under the License.
    19. */
    20.  
    21. using System.Collections.Generic;
    22. using UnityEngine.EventSystems;
    23. using UnityEngine;
    24. using UnityEngine.Assertions;
    25. using System;
    26.  
    27. namespace Oculus.Interaction
    28. {
    29.     public class PointableCanvasEventArgs
    30.     {
    31.         public readonly Canvas Canvas;
    32.         public readonly GameObject Hovered;
    33.         public readonly bool Dragging;
    34.  
    35.         public PointableCanvasEventArgs(Canvas canvas, GameObject hovered, bool dragging)
    36.         {
    37.             Canvas = canvas;
    38.             Hovered = hovered;
    39.             Dragging = dragging;
    40.         }
    41.     }
    42.  
    43.     /// <summary>
    44.     /// IPointerInteractableModule manages all InteractableCanvas events in
    45.     /// the scene and translates them into pointer events for Unity Canvas UIs.
    46.     /// </summary>
    47.     public class PointableCanvasModule : PointerInputModule
    48.     {
    49.         public static event Action<PointableCanvasEventArgs> WhenSelected;
    50.  
    51.         public static event Action<PointableCanvasEventArgs> WhenUnselected;
    52.  
    53.         public static event Action<PointableCanvasEventArgs> WhenSelectableHovered;
    54.  
    55.         public static event Action<PointableCanvasEventArgs> WhenSelectableUnhovered;
    56.  
    57.         [Tooltip("If true, the initial press position will be used as the drag start " +
    58.             "position, rather than the position when drag threshold is exceeded. This is used " +
    59.             "to prevent the pointer position shifting relative to the surface while dragging.")]
    60.         [SerializeField]
    61.         private bool _useInitialPressPositionForDrag = true;
    62.  
    63.         private Camera _pointerEventCamera;
    64.         private static PointableCanvasModule _instance = null;
    65.         private static PointableCanvasModule Instance
    66.         {
    67.             get
    68.             {
    69.                 if (_instance == null)
    70.                 {
    71.                     _instance = FindObjectOfType<PointableCanvasModule>();
    72.                 }
    73.                 return _instance;
    74.             }
    75.         }
    76.  
    77.         public static void RegisterPointableCanvas(IPointableCanvas pointerCanvas)
    78.         {
    79.             Assert.IsNotNull(Instance, $"A <b>{nameof(PointableCanvasModule)}</b> is required in the scene.");
    80.             Instance.AddPointerCanvas(pointerCanvas);
    81.         }
    82.  
    83.         public static void UnregisterPointableCanvas(IPointableCanvas pointerCanvas)
    84.         {
    85.             Instance?.RemovePointerCanvas(pointerCanvas);
    86.         }
    87.  
    88.         private Dictionary<int, Pointer> _pointerMap = new Dictionary<int, Pointer>();
    89.         private List<RaycastResult> _raycastResultCache = new List<RaycastResult>();
    90.         private List<Pointer> _pointersForDeletion = new List<Pointer>();
    91.         private Dictionary<IPointableCanvas, Action<PointerEvent>> _pointerCanvasActionMap =
    92.             new Dictionary<IPointableCanvas, Action<PointerEvent>>();
    93.  
    94.         private void AddPointerCanvas(IPointableCanvas pointerCanvas)
    95.         {
    96.             Action<PointerEvent> pointerCanvasAction = (args) => HandlePointerEvent(pointerCanvas.Canvas, args);
    97.             _pointerCanvasActionMap.Add(pointerCanvas, pointerCanvasAction);
    98.             pointerCanvas.WhenPointerEventRaised += pointerCanvasAction;
    99.         }
    100.  
    101.         private void RemovePointerCanvas(IPointableCanvas pointerCanvas)
    102.         {
    103.             Action<PointerEvent> pointerCanvasAction = _pointerCanvasActionMap[pointerCanvas];
    104.             _pointerCanvasActionMap.Remove(pointerCanvas);
    105.             pointerCanvas.WhenPointerEventRaised -= pointerCanvasAction;
    106.  
    107.             List<int> pointerIDs = new List<int>(_pointerMap.Keys);
    108.             foreach (int pointerID in pointerIDs)
    109.             {
    110.                 Pointer pointer = _pointerMap[pointerID];
    111.                 if (pointer.Canvas != pointerCanvas.Canvas)
    112.                 {
    113.                     continue;
    114.                 }
    115.                 ClearPointerSelection(pointer.PointerEventData);
    116.                 pointer.MarkForDeletion();
    117.                 _pointersForDeletion.Add(pointer);
    118.                 _pointerMap.Remove(pointerID);
    119.             }
    120.         }
    121.  
    122.         private void HandlePointerEvent(Canvas canvas, PointerEvent evt)
    123.         {
    124.             Pointer pointer;
    125.  
    126.             switch (evt.Type)
    127.             {
    128.                 case PointerEventType.Hover:
    129.                     pointer = new Pointer(canvas);
    130.                     pointer.PointerEventData = new PointerEventData(eventSystem);
    131.                     pointer.SetPosition(evt.Pose.position);
    132.                     _pointerMap.Add(evt.Identifier, pointer);
    133.                     break;
    134.                 case PointerEventType.Unhover:
    135.                     pointer = _pointerMap[evt.Identifier];
    136.                     _pointerMap.Remove(evt.Identifier);
    137.                     pointer.MarkForDeletion();
    138.                     _pointersForDeletion.Add(pointer);
    139.                     break;
    140.                 case PointerEventType.Select:
    141.                     pointer = _pointerMap[evt.Identifier];
    142.                     pointer.SetPosition(evt.Pose.position);
    143.                     pointer.Press();
    144.                     break;
    145.                 case PointerEventType.Unselect:
    146.                     pointer = _pointerMap[evt.Identifier];
    147.                     pointer.SetPosition(evt.Pose.position);
    148.                     pointer.Release();
    149.                     break;
    150.                 case PointerEventType.Move:
    151.                     pointer = _pointerMap[evt.Identifier];
    152.                     pointer.SetPosition(evt.Pose.position);
    153.                     break;
    154.                 case PointerEventType.Cancel:
    155.                     pointer = _pointerMap[evt.Identifier];
    156.                     _pointerMap.Remove(evt.Identifier);
    157.                     ClearPointerSelection(pointer.PointerEventData);
    158.                     pointer.MarkForDeletion();
    159.                     _pointersForDeletion.Add(pointer);
    160.                     break;
    161.             }
    162.         }
    163.  
    164.         /// <summary>
    165.         /// Pointer class that is used for state associated with IPointables that are currently
    166.         /// tracked by any IPointableCanvases in the scene.
    167.         /// </summary>
    168.         private class Pointer
    169.         {
    170.             public PointerEventData PointerEventData { get; set; }
    171.  
    172.             public bool MarkedForDeletion { get; private set; }
    173.  
    174.             private Canvas _canvas;
    175.             public Canvas Canvas => _canvas;
    176.  
    177.             private Vector3 _position;
    178.             public Vector3 Position => _position;
    179.  
    180.             private GameObject _hoveredSelectable;
    181.             public GameObject HoveredSelectable => _hoveredSelectable;
    182.  
    183.  
    184.             private bool _pressing = false;
    185.             private bool _pressed;
    186.             private bool _released;
    187.  
    188.             public Pointer(Canvas canvas)
    189.             {
    190.                 _canvas = canvas;
    191.                 _pressed = _released = false;
    192.             }
    193.  
    194.             public void Press()
    195.             {
    196.                 if (_pressing) return;
    197.                 _pressing = true;
    198.                 _pressed = true;
    199.             }
    200.             public void Release()
    201.             {
    202.                 if (!_pressing) return;
    203.                 _pressing = false;
    204.                 _released = true;
    205.             }
    206.  
    207.             public void ReadAndResetPressedReleased(out bool pressed, out bool released)
    208.             {
    209.                 pressed = _pressed;
    210.                 released = _released;
    211.                 _pressed = _released = false;
    212.             }
    213.  
    214.             public void MarkForDeletion()
    215.             {
    216.                 MarkedForDeletion = true;
    217.                 Release();
    218.             }
    219.  
    220.             public void SetPosition(Vector3 position)
    221.             {
    222.                 _position = position;
    223.             }
    224.  
    225.             public void SetHoveredSelectable(GameObject hoveredSelectable)
    226.             {
    227.                 _hoveredSelectable = hoveredSelectable;
    228.             }
    229.         }
    230.  
    231.         protected bool _started = false;
    232.  
    233.         protected override void Start()
    234.         {
    235.             this.BeginStart(ref _started, () => base.Start());
    236.             this.EndStart(ref _started);
    237.         }
    238.  
    239.         protected override void OnEnable()
    240.         {
    241.             base.OnEnable();
    242.             if (_started)
    243.             {
    244.                 _pointerEventCamera = gameObject.AddComponent<Camera>();
    245.                 _pointerEventCamera.nearClipPlane = 0.1f;
    246.  
    247.                 // We do not need this camera to be enabled to serve this module's purposes:
    248.                 // as a dependency for Canvases and for its WorldToScreenPoint functionality
    249.                 _pointerEventCamera.enabled = false;
    250.             }
    251.         }
    252.  
    253.         protected override void OnDisable()
    254.         {
    255.             if (_started)
    256.             {
    257.                 Destroy(_pointerEventCamera);
    258.                 _pointerEventCamera = null;
    259.             }
    260.             base.OnDisable();
    261.         }
    262.  
    263.         // Based On FindFirstRaycast
    264.         protected static RaycastResult FindFirstRaycastWithinCanvas(List<RaycastResult> candidates, Canvas canvas)
    265.         {
    266.             GameObject candidateGameObject;
    267.             Canvas candidateCanvas;
    268.             for (var i = 0; i < candidates.Count; ++i)
    269.             {
    270.                 candidateGameObject = candidates[i].gameObject;
    271.                 if (candidateGameObject == null) continue;
    272.  
    273.                 candidateCanvas = candidateGameObject.GetComponentInParent<Canvas>();
    274.                 if (candidateCanvas == null) continue;
    275.                 if (candidateCanvas.rootCanvas != canvas) continue;
    276.  
    277.                 return candidates[i];
    278.             }
    279.             return new RaycastResult();
    280.         }
    281.  
    282.         private void UpdateRaycasts(Pointer pointer, out bool pressed, out bool released)
    283.         {
    284.             PointerEventData pointerEventData = pointer.PointerEventData;
    285.  
    286.             Vector2 prevPosition = pointerEventData.position;
    287.             Canvas canvas = pointer.Canvas;
    288.             canvas.worldCamera = _pointerEventCamera;
    289.  
    290.             pointerEventData.Reset();
    291.  
    292.             pointer.ReadAndResetPressedReleased(out pressed, out released);
    293.  
    294.             Vector3 position = Vector3.zero;
    295.             var plane = new Plane(-1f * canvas.transform.forward, canvas.transform.position);
    296.             var ray = new Ray(pointer.Position - canvas.transform.forward, canvas.transform.forward);
    297.  
    298.             float enter;
    299.             if (plane.Raycast(ray, out enter))
    300.             {
    301.                 position = ray.GetPoint(enter);
    302.             }
    303.  
    304.             // We need to position our camera at an offset from the Pointer position or else
    305.             // a graphic raycast may ignore a world canvas that's outside of our regular camera view(s)
    306.             _pointerEventCamera.transform.position = pointer.Position - canvas.transform.forward;
    307.             _pointerEventCamera.transform.LookAt(pointer.Position, canvas.transform.up);
    308.  
    309.             Vector2 pointerPosition = _pointerEventCamera.WorldToScreenPoint(position);
    310.             pointerEventData.position = pointerPosition;
    311.  
    312.             // RaycastAll raycasts against with every GraphicRaycaster in the scene,
    313.             // including nested ones like in the case of a dropdown
    314.             eventSystem.RaycastAll(pointerEventData, _raycastResultCache);
    315.  
    316.             RaycastResult firstResult = FindFirstRaycastWithinCanvas(_raycastResultCache, canvas);
    317.             pointer.PointerEventData.pointerCurrentRaycast = firstResult;
    318.  
    319.             _raycastResultCache.Clear();
    320.  
    321.             // We use a static translation offset from the canvas for 2D position delta tracking
    322.             _pointerEventCamera.transform.position = canvas.transform.position - canvas.transform.forward;
    323.             _pointerEventCamera.transform.LookAt(canvas.transform.position, canvas.transform.up);
    324.  
    325.             pointerPosition = _pointerEventCamera.WorldToScreenPoint(position);
    326.             pointerEventData.position = pointerPosition;
    327.  
    328.             if (pressed)
    329.             {
    330.                 pointerEventData.delta = Vector2.zero;
    331.             }
    332.             else
    333.             {
    334.                 pointerEventData.delta = pointerEventData.position - prevPosition;
    335.             }
    336.  
    337.             pointerEventData.button = PointerEventData.InputButton.Left;
    338.         }
    339.  
    340.         public override void Process()
    341.         {
    342.             foreach (Pointer pointer in _pointersForDeletion)
    343.             {
    344.                 ProcessPointer(pointer, true);
    345.             }
    346.             _pointersForDeletion.Clear();
    347.  
    348.             foreach (Pointer pointer in _pointerMap.Values)
    349.             {
    350.                 ProcessPointer(pointer);
    351.             }
    352.         }
    353.  
    354.         private void ProcessPointer(Pointer pointer, bool forceRelease = false)
    355.         {
    356.             bool pressed = false;
    357.             bool released = false;
    358.             bool wasDragging = pointer.PointerEventData.dragging;
    359.  
    360.             UpdateRaycasts(pointer, out pressed, out released);
    361.  
    362.             PointerEventData pointerEventData = pointer.PointerEventData;
    363.             UpdatePointerEventData(pointerEventData, pressed, released);
    364.  
    365.             released |= forceRelease;
    366.  
    367.             if (!released)
    368.             {
    369.                 ProcessMove(pointerEventData);
    370.                 ProcessDrag(pointerEventData);
    371.             }
    372.             else
    373.             {
    374.                 HandlePointerExitAndEnter(pointerEventData, null);
    375.                 RemovePointerData(pointerEventData);
    376.             }
    377.  
    378.             HandleSelectableHover(pointer, wasDragging);
    379.             HandleSelectablePress(pointer, pressed, released, wasDragging);
    380.         }
    381.  
    382.         private void HandleSelectableHover(Pointer pointer, bool wasDragging)
    383.         {
    384.             bool dragging = pointer.PointerEventData.dragging || wasDragging;
    385.  
    386.             GameObject currentOverGo = pointer.PointerEventData.pointerCurrentRaycast.gameObject;
    387.             GameObject prevHoveredSelectable = pointer.HoveredSelectable;
    388.             GameObject newHoveredSelectable = ExecuteEvents.GetEventHandler<ISelectHandler>(currentOverGo);
    389.             pointer.SetHoveredSelectable(newHoveredSelectable);
    390.  
    391.             if (newHoveredSelectable != null && newHoveredSelectable != prevHoveredSelectable)
    392.             {
    393.                 WhenSelectableHovered?.Invoke(new PointableCanvasEventArgs(pointer.Canvas, pointer.HoveredSelectable, dragging));
    394.             }
    395.             else if (prevHoveredSelectable != null && newHoveredSelectable == null)
    396.             {
    397.                 WhenSelectableUnhovered?.Invoke(new PointableCanvasEventArgs(pointer.Canvas, pointer.HoveredSelectable, dragging));
    398.             }
    399.         }
    400.  
    401.         private void HandleSelectablePress(Pointer pointer, bool pressed, bool released, bool wasDragging)
    402.         {
    403.             bool dragging = pointer.PointerEventData.dragging || wasDragging;
    404.  
    405.             if (pressed)
    406.             {
    407.                 WhenSelected?.Invoke(new PointableCanvasEventArgs(pointer.Canvas, pointer.HoveredSelectable, dragging));
    408.             }
    409.             else if (released && !pointer.MarkedForDeletion)
    410.             {
    411.                 // Unity handles UI selection on release, so we verify the hovered element has been selected
    412.                 bool hasSelectedHoveredObject = pointer.HoveredSelectable != null &&
    413.                                                 pointer.HoveredSelectable == pointer.PointerEventData.selectedObject;
    414.                 GameObject selectedObject = hasSelectedHoveredObject ? pointer.HoveredSelectable : null;
    415.                 WhenUnselected?.Invoke(new PointableCanvasEventArgs(pointer.Canvas, selectedObject, dragging));
    416.             }
    417.         }
    418.  
    419.         /// <summary>
    420.         /// This method is based on ProcessTouchPoint in StandaloneInputModule,
    421.         /// but is instead used for Pointer events
    422.         /// </summary>
    423.         protected void UpdatePointerEventData(PointerEventData pointerEvent, bool pressed, bool released)
    424.         {
    425.             var currentOverGo = pointerEvent.pointerCurrentRaycast.gameObject;
    426.  
    427.             // PointerDown notification
    428.             if (pressed)
    429.             {
    430.                 pointerEvent.eligibleForClick = true;
    431.                 pointerEvent.delta = Vector2.zero;
    432.                 pointerEvent.dragging = false;
    433.                 pointerEvent.useDragThreshold = true;
    434.                 pointerEvent.pressPosition = pointerEvent.position;
    435.                 pointerEvent.pointerPressRaycast = pointerEvent.pointerCurrentRaycast;
    436.  
    437.                 DeselectIfSelectionChanged(currentOverGo, pointerEvent);
    438.  
    439.                 if (pointerEvent.pointerEnter != currentOverGo)
    440.                 {
    441.                     // send a pointer enter to the touched element if it isn't the one to select...
    442.                     HandlePointerExitAndEnter(pointerEvent, currentOverGo);
    443.                     pointerEvent.pointerEnter = currentOverGo;
    444.                 }
    445.  
    446.                 // search for the control that will receive the press
    447.                 // if we can't find a press handler set the press
    448.                 // handler to be what would receive a click.
    449.                 var newPressed = ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.pointerDownHandler);
    450.  
    451.                 // didnt find a press handler... search for a click handler
    452.                 if (newPressed == null)
    453.                     newPressed = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
    454.  
    455.                 float time = Time.unscaledTime;
    456.  
    457.                 if (newPressed == pointerEvent.lastPress)
    458.                 {
    459.                     var diffTime = time - pointerEvent.clickTime;
    460.                     if (diffTime < 0.3f)
    461.                         ++pointerEvent.clickCount;
    462.                     else
    463.                         pointerEvent.clickCount = 1;
    464.  
    465.                     pointerEvent.clickTime = time;
    466.                 }
    467.                 else
    468.                 {
    469.                     pointerEvent.clickCount = 1;
    470.                 }
    471.  
    472.                 pointerEvent.pointerPress = newPressed;
    473.                 pointerEvent.rawPointerPress = currentOverGo;
    474.  
    475.                 pointerEvent.clickTime = time;
    476.  
    477.                 // Save the drag handler as well
    478.                 pointerEvent.pointerDrag = ExecuteEvents.GetEventHandler<IDragHandler>(currentOverGo);
    479.  
    480.                 if (pointerEvent.pointerDrag != null)
    481.                     ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.initializePotentialDrag);
    482.  
    483.             }
    484.  
    485.             // PointerUp notification
    486.             if (released)
    487.             {
    488.                 ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler);
    489.  
    490.                 // see if we mouse up on the same element that we clicked on...
    491.                 var pointerUpHandler = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
    492.  
    493.                 // PointerClick and Drop events
    494.                 if (pointerEvent.pointerPress == pointerUpHandler && pointerEvent.eligibleForClick)
    495.                 {
    496.                     ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerClickHandler);
    497.                 }
    498.  
    499.                 if (pointerEvent.pointerDrag != null && pointerEvent.dragging)
    500.                 {
    501.                     ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.dropHandler);
    502.                 }
    503.  
    504.                 pointerEvent.eligibleForClick = false;
    505.                 pointerEvent.pointerPress = null;
    506.                 pointerEvent.rawPointerPress = null;
    507.  
    508.                 if (pointerEvent.pointerDrag != null && pointerEvent.dragging)
    509.                     ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.endDragHandler);
    510.  
    511.                 pointerEvent.dragging = false;
    512.                 pointerEvent.pointerDrag = null;
    513.  
    514.                 // send exit events as we need to simulate this on touch up on touch device
    515.                 ExecuteEvents.ExecuteHierarchy(pointerEvent.pointerEnter, pointerEvent, ExecuteEvents.pointerExitHandler);
    516.                 pointerEvent.pointerEnter = null;
    517.             }
    518.         }
    519.  
    520.         /// <summary>
    521.         /// Override of PointerInputModule's ProcessDrag to allow using the initial press position for drag begin.
    522.         /// Set _useInitialPressPositionForDrag to false if you prefer the default behaviour of PointerInputModule.
    523.         /// </summary>
    524.         protected override void ProcessDrag(PointerEventData pointerEvent)
    525.         {
    526.             if (!pointerEvent.IsPointerMoving() ||
    527.                 Cursor.lockState == CursorLockMode.Locked ||
    528.                 pointerEvent.pointerDrag == null)
    529.                 return;
    530.  
    531.             if (!pointerEvent.dragging
    532.                 && ShouldStartDrag(pointerEvent.pressPosition, pointerEvent.position,
    533.                     eventSystem.pixelDragThreshold, pointerEvent.useDragThreshold))
    534.             {
    535.                 if (_useInitialPressPositionForDrag)
    536.                 {
    537.                     pointerEvent.position = pointerEvent.pressPosition;
    538.                 }
    539.                 ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent,
    540.                     ExecuteEvents.beginDragHandler);
    541.                 pointerEvent.dragging = true;
    542.             }
    543.  
    544.             // Drag notification
    545.             if (pointerEvent.dragging)
    546.             {
    547.                 // Before doing drag we should cancel any pointer down state
    548.                 // And clear selection!
    549.                 if (pointerEvent.pointerPress != pointerEvent.pointerDrag)
    550.                 {
    551.                     ClearPointerSelection(pointerEvent);
    552.                 }
    553.  
    554.                 ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent,
    555.                     ExecuteEvents.dragHandler);
    556.             }
    557.         }
    558.  
    559.         private void ClearPointerSelection(PointerEventData pointerEvent)
    560.         {
    561.             ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent,
    562.                 ExecuteEvents.pointerUpHandler);
    563.  
    564.             pointerEvent.eligibleForClick = false;
    565.             pointerEvent.pointerPress = null;
    566.             pointerEvent.rawPointerPress = null;
    567.         }
    568.  
    569.         /// <summary>
    570.         /// Used in PointerInputModule's ProcessDrag implementation. Brought into this subclass with a protected
    571.         /// signature (as opposed to the parent's private signature) to be used in this subclass's overridden ProcessDrag.
    572.         /// </summary>
    573.         protected static bool ShouldStartDrag(Vector2 pressPos, Vector2 currentPos, float threshold, bool useDragThreshold)
    574.         {
    575.             if (!useDragThreshold)
    576.                 return true;
    577.  
    578.             return (pressPos - currentPos).sqrMagnitude >= threshold * threshold;
    579.         }
    580.     }
    581. }
    582.