Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Bug PointerMoveEvent.localPosition returns incorrect value

Discussion in 'UI Toolkit' started by ShadyMike, Jan 4, 2022.

  1. ShadyMike

    ShadyMike

    Joined:
    Apr 14, 2013
    Posts:
    60
    When installing two events on a VisualElement, `PointerDownEvent` and `PointerMoveEvent`, and printing `ev.localPosition` in both of them, I get the following output when I click and move my mouse just a few pixels:


    Down: (912.87, 176.55, 0.00)
    Move: (912.87, 447.90, 0.00)


    I am implementing a virtual joystick for mobile but this bug prevents me from doing so, unless I am missing something. I will post the code below in case it's my fault or someone knows a workaround.

    Put this script on the same GameObject that has the `UIDocument` component. The document must contain a root VisualElement that covers 100% of the screen, a child element called "JoystickContainer" that covers maybe 1/2 half of the screen (area where the joystick can be used), and that container must have two children "JoystickBackground" and "JoystickHandle" that are just two images with the same size.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UIElements;
    5.  
    6. public class JoystickUI : MonoBehaviour
    7. {
    8.     public float joystickRadius = 150f;
    9.     public Vector2 initialLocalPosition = new Vector2(180f, 450f);
    10.  
    11.     VisualElement joystickBackground;
    12.     VisualElement joystickHandle;
    13.     Vector2 backgroundLocalPosition;
    14.     Vector2 handleLocalPosition;
    15.  
    16.     // If the joystick is controlled by the user right now.
    17.     bool isActive;
    18.     int activePointerId;
    19.  
    20.     void Start()
    21.     {
    22.         UIDocument document = GetComponent<UIDocument>();
    23.         VisualElement root = document.rootVisualElement;
    24.  
    25.         VisualElement joystickContainer = root.Query("JoystickContainer");
    26.         joystickContainer.RegisterCallback<PointerDownEvent>(ev => OnPointerDown(ev));
    27.         root.RegisterCallback<PointerUpEvent>(ev => OnPointerUp(ev));
    28.         root.RegisterCallback<PointerMoveEvent>(ev => OnPointerMove(ev));
    29.  
    30.         joystickBackground = root.Query("JoystickBackground");
    31.         joystickHandle = root.Query("JoystickHandle");
    32.         SetBackgroundPosition(initialLocalPosition);
    33.         SetHandlePosition(initialLocalPosition);
    34.     }
    35.  
    36.     void SetBackgroundPositionCentered(Vector2 localPosition)
    37.     {
    38.         backgroundLocalPosition = localPosition;
    39.  
    40.         joystickBackground.style.left =
    41.             backgroundLocalPosition.x - joystickBackground.resolvedStyle.width / 2f;
    42.         joystickBackground.style.top =
    43.             backgroundLocalPosition.y - joystickBackground.resolvedStyle.height / 2f;
    44.     }
    45.  
    46.     void SetHandlePositionCentered(Vector2 localPosition)
    47.     {
    48.         handleLocalPosition = localPosition;
    49.  
    50.         joystickHandle.style.left =
    51.             handleLocalPosition.x - joystickHandle.resolvedStyle.width / 2f;
    52.         joystickHandle.style.top =
    53.             handleLocalPosition.y - joystickHandle.resolvedStyle.height / 2f;
    54.     }
    55.  
    56.     void SetBackgroundPosition(Vector2 localPosition)
    57.     {
    58.         backgroundLocalPosition = localPosition;
    59.  
    60.         joystickBackground.style.left = backgroundLocalPosition.x;
    61.         joystickBackground.style.top = backgroundLocalPosition.y;
    62.     }
    63.  
    64.     void SetHandlePosition(Vector2 localPosition)
    65.     {
    66.         handleLocalPosition = localPosition;
    67.  
    68.         joystickHandle.style.left = handleLocalPosition.x;
    69.         joystickHandle.style.top = handleLocalPosition.y;
    70.     }
    71.  
    72.     void OnPointerDown(PointerDownEvent ev)
    73.     {
    74.         if (isActive)
    75.             return;
    76.  
    77.         isActive = true;
    78.         activePointerId = ev.pointerId;
    79.         SetBackgroundPositionCentered(ev.localPosition);
    80.         SetHandlePositionCentered(ev.localPosition);
    81.         Debug.Log("Down: " + ev.localPosition);
    82.     }
    83.  
    84.     void OnPointerUp(PointerUpEvent ev)
    85.     {
    86.         if (ev.pointerId != activePointerId || !isActive)
    87.             return;
    88.  
    89.         isActive = false;
    90.         SetBackgroundPosition(initialLocalPosition);
    91.         SetHandlePosition(initialLocalPosition);
    92.     }
    93.  
    94.     void OnPointerMove(PointerMoveEvent ev)
    95.     {
    96.         if (ev.pointerId != activePointerId || !isActive)
    97.             return;
    98.        
    99.         // Demonstrate that `ev.localPosition` returns a wrong value.
    100.         SetHandlePositionCentered(ev.localPosition);
    101.         Debug.Log("Move: " + ev.localPosition);
    102.     }
    103. }
     
  2. uBenoitA

    uBenoitA

    Unity Technologies

    Joined:
    Apr 15, 2020
    Posts:
    198
    The first thing that stands out to me is that OnPointerDown is registered on the joystickContainer, while OnPointerMove is on the root. The
    localPosition
    is measured relative to the event's
    currentTarget
    , which is not the same, hence the not-same positions, most likely.

    Could you verify that there's no bug by also printing
    ev.position
    and confirming that they are indeed the same? If you want to have a local position relative to a given fixed element, you can use the same calculation as is done when we set the current target:
    localPosition = element.WorldToLocal(position);
    .
     
    ShadyMike likes this.
  3. ShadyMike

    ShadyMike

    Joined:
    Apr 14, 2013
    Posts:
    60
    Thanks a lot! I totally missed that.

    Yes they are the same, it's not a bug. Thanks again.