unity-game-enginecanvascontrolstouchjoystick

Joystick and touchpoint are misaligned


I'm having a problem with custom joystick I made by following this tutorial and making a couple of changes later to convert it to fixed joystick instead of floating one.

The joystick works as expected on 2640x1200 display (that's resolution I use for canvas scaler), but when testing on other screens with bigger or smaller resolution, joystick knob/mushroom and touchpoint are misaligned.

Expected result.

Example of misalignment.

Code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using ETouch = UnityEngine.InputSystem.EnhancedTouch;

public class UI_Game_Controls : MonoBehaviour
{
    [SerializeField] private GameObject Joystick_Walk_Area;
    [SerializeField] private GameObject Joystick_Walk_Mushroom;
    private ETouch.Finger Walking_Finger;

    Vector2 Walk_Area_Center;

    float Walk_Area_Width;
    float Walk_Area_Anchor_X;
    float Walk_Area_Height;
    float Walk_Area_Anchor_Y;

    float Scale_Factor;


    private void OnEnable()
    {
        //Debug.Log(Joystick_Walk_Mushroom.GetComponent<RectTransform>().anchoredPosition);
        //Walk_Area_Center = Joystick_Walk_Area.GetComponent<RectTransform>().anchoredPosition + new Vector2(250, 250);
        //Debug.Log(Walk_Area_Center);
        
        /*Gets 4 points used for checking if touchpoint is inside joystick area.*/
        Walk_Area_Width = Joystick_Walk_Area.GetComponent<RectTransform>().anchoredPosition.x + Joystick_Walk_Area.GetComponent<RectTransform>().rect.width;
        Walk_Area_Anchor_X = Joystick_Walk_Area.GetComponent<RectTransform>().anchoredPosition.x;
        Walk_Area_Height = Joystick_Walk_Area.GetComponent<RectTransform>().anchoredPosition.y + Joystick_Walk_Area.GetComponent<RectTransform>().rect.height;
        Walk_Area_Anchor_Y = Joystick_Walk_Area.GetComponent<RectTransform>().anchoredPosition.y;
        

        ETouch.EnhancedTouchSupport.Enable();
        ETouch.Touch.onFingerDown += HandleFingerDown;
        ETouch.Touch.onFingerMove += HandleFingerMove;
        ETouch.Touch.onFingerUp += Touch_onFingerUp;
    }

    private void OnDisable()
    {
        ETouch.Touch.onFingerDown -= HandleFingerDown;
        ETouch.Touch.onFingerMove -= HandleFingerMove;
        ETouch.Touch.onFingerUp -= Touch_onFingerUp;
        ETouch.EnhancedTouchSupport.Disable();
    }

    private void HandleFingerDown(ETouch.Finger FingerTouchDown)
    {
        // Checks if user has touched joystick.
        if (Walking_Finger == null &&
            FingerTouchDown.screenPosition.x >= Walk_Area_Anchor_X && FingerTouchDown.screenPosition.x <= Walk_Area_Width &&
            FingerTouchDown.screenPosition.y >= Walk_Area_Anchor_Y && FingerTouchDown.screenPosition.y <= Walk_Area_Height)
        {
            Walking_Finger = FingerTouchDown;
        }
    }

    private void HandleFingerMove(ETouch.Finger FingerTouchMove)
    {
        if(FingerTouchMove == Walking_Finger)
        {
            Vector2 Walk_Mushroom_Position;
            // Find size of joystick area in which knob should move.
            float Max_Movement = Joystick_Walk_Area.GetComponent<RectTransform>().sizeDelta.y / 2; // 250;

            /*if (Vector2.Distance(FingerTouchMove.currentTouch.screenPosition,Walk_Area_Center)
                > Max_Movement)*/
            if (false) // Disabled on purpose.
            {
                Walk_Mushroom_Position = (FingerTouchMove.currentTouch.screenPosition -
                    Walk_Area_Center).normalized * Max_Movement;
            }
            else
            {
                Walk_Mushroom_Position = FingerTouchMove.currentTouch.screenPosition - Walk_Area_Center;
            }
            Joystick_Walk_Mushroom.GetComponent<RectTransform>().anchoredPosition = Walk_Mushroom_Position;
        }
    }

    private void Touch_onFingerUp(ETouch.Finger obj)
    {
        Walking_Finger = null;
        Joystick_Walk_Mushroom.GetComponent<RectTransform>().anchoredPosition = Vector2.zero;
    }

    private void Start()
    {
        // Get scale factor of canvas.
        Scale_Factor = this.GetComponent<Canvas>().scaleFactor;
        // Calculate center of joystick area after scaling canvas.
        Walk_Area_Center = (Joystick_Walk_Area.GetComponent<RectTransform>().anchoredPosition + new Vector2(250, 250)) * Scale_Factor;

        // // Calculate 4 points after scaling canvas.
        Walk_Area_Width = Walk_Area_Width * Scale_Factor;
        Walk_Area_Anchor_X = Walk_Area_Anchor_X * Scale_Factor;
        Walk_Area_Height = Walk_Area_Height * Scale_Factor;
        Walk_Area_Anchor_Y = Walk_Area_Anchor_Y * Scale_Factor;
    }

    private void Update()
    {
        //Debug.Log(Joystick_Walk_Mushroom.GetComponent<RectTransform>().anchoredPosition.normalized);
        //Debug.Log("Vector.normalized" + (new Vector2(600,600) - new Vector2(300,300)).normalized);

        if (Walking_Finger != null)
        {
            Debug.Log("Finger" + Walking_Finger.currentTouch.screenPosition);
            Debug.Log("Joystick" + Joystick_Walk_Mushroom.GetComponent<RectTransform>().anchoredPosition);
        }
    }
}

I'm super inexperienced with both c# and unity so please excuse me if I'm making some obvious mistake.

P.S. Sorry for awful and barely commented code.


Solution

  • Got it, I added RectTransformUtility.ScreenPointToLocalPointInRectangle inside HandleFingerMove method.

    Old:

        private void HandleFingerMove(ETouch.Finger FingerTouchMove)
            {
                if(FingerTouchMove == Walking_Finger)
                {
                    Vector2 Walk_Mushroom_Position;
                    // Find size of joystick area in which knob should move.
                    float Max_Movement = Joystick_Walk_Area.GetComponent<RectTransform>().sizeDelta.y / 2; // 250;
         
                    /*if (Vector2.Distance(FingerTouchMove.currentTouch.screenPosition,Walk_Area_Center)
                        > Max_Movement)*/
                    if (false) // Disabled on purpose.
                    {
                        Walk_Mushroom_Position = (FingerTouchMove.currentTouch.screenPosition -
                            Walk_Area_Center).normalized * Max_Movement;
                    }
                    else
                    {
                        Walk_Mushroom_Position = FingerTouchMove.currentTouch.screenPosition - Walk_Area_Center;
                    }
                    Joystick_Walk_Mushroom.GetComponent<RectTransform>().anchoredPosition = Walk_Mushroom_Position;
                }
            }
    
    

    New:

        private void HandleFingerMove(ETouch.Finger FingerTouchMove)
            {
                if(FingerTouchMove == Walking_Finger)
                {
                    Vector2 Walk_Mushroom_Position;
                  // Find size of joystick area in which knob should move.
                    float Max_Movement = Joystick_Walk_Area.GetComponent<RectTransform>().sizeDelta.y / 2; // 250;
         
                    if (Vector2.Distance(FingerTouchMove.currentTouch.screenPosition,Walk_Area_Center)
                        > Max_Movement)
                    //if (false)
                    {
                        //TO DO
                        Debug.Log("Finger out");
                        Walk_Mushroom_Position = (FingerTouchMove.currentTouch.screenPosition -
                            Walk_Area_Center).normalized * Max_Movement;
                    }
                    else
                    {
                        RectTransformUtility.ScreenPointToLocalPointInRectangle(
                            Joystick_Walk_Area.GetComponent<RectTransform>(),
                            FingerTouchMove.currentTouch.screenPosition,
                            this.GetComponent<Canvas>().worldCamera,
                            out Walk_Mushroom_Position);
         
                        Debug.Log(Screen.currentResolution);
         
                        Walk_Mushroom_Position.x -= Max_Movement;
                        Walk_Mushroom_Position.y -= Max_Movement;
                    }
                    Joystick_Walk_Mushroom.GetComponent<RectTransform>().anchoredPosition = Walk_Mushroom_Position;
                }
            }
    
    

    Edit:

    I will try to explain what (I think) I did and why I did things this way.

    My scene is organised like this:

    Scene
    
    |
    
    > Canvas_UI
    
      |
    
      > Joystick_Walk_Area
    
        |
    
        > Joystick_Walk_Mushroom
    

    Class which contains HandleFingerMove method is part of Canvas_UI. Canvas_UI is referenced as this inside class.

    Joystick_Walk_Area is child of Canvas_UI. Joystick_Walk_Area is anchored in bottom left corner of canvas (75px from left edge and 50px above bottom edge of canvas) and has dimensions of 500px x 500px.

    Joystick_Walk_Mushroom is child of Joystick_Walk_Area and its anchor is in center of Joystick_Walk_Area.

    I used RectTransformUtility.ScreenPointToLocalPointInRectangle to convert FingerTouchMove.currentTouch.screenPosition (user touch input) into local point of Joystick_Walk_Areawhich made Joystick_Walk_Mushroom appear +250px in X & Y directions away from FingerTouchMove.currentTouch.screenPosition, I fixed this by subtracting touch coordinates (lines 28 and 29)