Search code examples
c#user-interfaceunity-game-enginejoystick

Rotate 2D Sprite with Virtual Joystick


I am trying to rotate a GameObject along z-axis using Joystick, but its rotating y-axis. I might missed some math calculation. Any Help?? Reference Image enter image description here

void FixedUpdate()
    {
        // get input from joystick
        // get input from joystick
        rightJoystickInput = rightJoystick.GetInputDirection();

        float xMovementRightJoystick = rightJoystickInput.x; // The horizontal movement from joystick 02
        float zMovementRightJoystick = rightJoystickInput.y; // The vertical movement from joystick 02


        // if there is only input from the right joystick
        if (rightJoystickInput != Vector3.zero)
        {
            // calculate the player's direction based on angle
            float tempAngle = Mathf.Atan2(zMovementRightJoystick, xMovementRightJoystick);
            xMovementRightJoystick *= Mathf.Abs(Mathf.Cos(tempAngle));
            zMovementRightJoystick *= Mathf.Abs(Mathf.Sin(tempAngle));

            // rotate the player to face the direction of input
            Vector3 temp = transform.position;
            temp.x += xMovementRightJoystick;
            temp.z += zMovementRightJoystick;
            Vector3 lookDirection = temp - transform.position;
            if (lookDirection != Vector3.zero)
            {
                rotationTarget.localRotation = Quaternion.Slerp(rotationTarget.localRotation, Quaternion.LookRotation(lookDirection) * Quaternion.Euler(0, 45f, 0), rotationSpeed * Time.deltaTime);
            }
        }
    }

Solution

  • You don't need most of the code in your question and this is really simple.

    1.Find the angle with Mathf.Atan2 then multiple it with Mathf.Rad2Deg.

    2.Use Quaternion.Euler(new Vector3(0, 0, angle)) to get the rotation then apply it to the Object.

    This should be one in the Update function not FixedUpdate because FixedUpdate is used to move Rigidbody Objects.

    public Transform rotationTarget;
    public bool flipRot = true;
    
    void Update()
    {
        rightJoystickInput = rightJoystick.GetInputDirection();
    
        float horizontal = rightJoystickInput.x;
        float vertical = rightJoystickInput.y;
    
        float angle = Mathf.Atan2(horizontal, vertical) * Mathf.Rad2Deg;
        angle = flipRot ? -angle : angle;
    
        rotationTarget.rotation = Quaternion.Euler(new Vector3(0, 0, angle));
    }
    

    If using Rigidbody2D then use Rigidbody2D.MoveRotation in the FixedUpdate function. The rest of the code stays the-same.

    public Rigidbody2D rg2d;
    public bool flipRot = true;
    
    void FixedUpdate()
    {
        rightJoystickInput = rightJoystick.GetInputDirection();
    
        float horizontal = rightJoystickInput.x;
        float vertical = rightJoystickInput.y;
    
        float angle = Mathf.Atan2(horizontal, vertical) * Mathf.Rad2Deg;
        angle = flipRot ? -angle : angle;
    
        rg2d.MoveRotation(angle);
    }
    

    EDIT:

    But only the problem is when i leave joystick its rotation is setting to 0 instantly which looks too odd. How can i fix it?

    You have to detect when you release the joystick in OnPointerUp then slowly lerp the joystick thump back to the Zero position. You also have to lerp the current target object angle to zero or to its default value and this should be done in a coroutine function. When OnPointerDown is called, stop the current coroutine function. Prevent the code in FixedUpdate from running when finger is released so that it won't interfere with the coroutine function.

    For the sake of completeness, below is the combination of a Joystick code and the Rigidbody answer above:

    public class VirtualJoystickController : MonoBehaviour,
        IDragHandler, IPointerUpHandler, IPointerDownHandler
    {
        private Image bgImg;
        private Image joystickImg;
        public float mas_distance = 7f;
    
        void Start()
        {
            bgImg = GameObject.Find("JoystickBGImage").GetComponent<Image>(); // the joysticks background
            joystickImg = GameObject.Find("Joystickthumb").GetComponent<Image>(); // the joystick object to use
        }
    
        private Vector3 _inputDirection = Vector3.zero;
    
        //the movementDirection
        public Vector3 joystickInputDirection
        {
            set
            {
                //Change only if value is different from old one
                if (_inputDirection != value)
                {
                    _inputDirection = value;
    
                    Debug.Log("Dir: " + _inputDirection);
                }
            }
    
            get
            {
                return _inputDirection;
            }
        }
    
        public void OnDrag(PointerEventData eventData)
        {
            dragJoyStick(eventData);
        }
    
        void dragJoyStick(PointerEventData eventData)
        {
            Vector3 tempDir = Vector3.zero;
    
            Vector2 pos = Vector2.zero;
            if (RectTransformUtility.ScreenPointToLocalPointInRectangle
                (bgImg.rectTransform,
                eventData.position,
                eventData.pressEventCamera,
                out pos))
            {
    
                pos.x = (pos.x / bgImg.rectTransform.sizeDelta.x);
                pos.y = (pos.y / bgImg.rectTransform.sizeDelta.y);
    
                float x = (bgImg.rectTransform.pivot.x == 1) ? pos.x * 2 + 1 : pos.x * 2 - 1;
                float y = (bgImg.rectTransform.pivot.y == 1) ? pos.y * 2 + 1 : pos.y * 2 - 1;
    
                tempDir = new Vector3(x, y, 0);
    
                if (tempDir.magnitude > 1)
                {
                    tempDir = tempDir.normalized;
                }
    
                joystickImg.rectTransform.anchoredPosition = new Vector3(
                  tempDir.x * (bgImg.rectTransform.sizeDelta.x / mas_distance),
                    tempDir.y * (bgImg.rectTransform.sizeDelta.y / mas_distance));
    
                joystickInputDirection = tempDir;
            }
        }
    
        public void OnPointerDown(PointerEventData eventData)
        {
            released = false;
            //Stop current coroutine
            if (retCoroutine != null)
                StopCoroutine(retCoroutine);
    
            if (eventData.pointerCurrentRaycast.gameObject == bgImg.gameObject ||
              eventData.pointerCurrentRaycast.gameObject == joystickImg.gameObject)
            {
                OnDrag(eventData);
            }
        }
    
        public void OnPointerUp(PointerEventData eventData)
        {
            released = true;
    
            //Stop current coroutine then start a new one
            if (retCoroutine != null)
                StopCoroutine(retCoroutine);
            retCoroutine = StartCoroutine(SlowReturn(returnTime));
        }
    
        IEnumerator SlowReturn(float duration)
        {
            RectTransform thumbstickTransform = joystickImg.rectTransform;
    
            Vector3 toPosition = Vector3.zero;
            float counter = 0;
    
            //Get the current position of the object to be moved
            Vector2 currentThumb = thumbstickTransform.anchoredPosition;
    
            while (counter < duration)
            {
                counter += Time.deltaTime;
    
                //Slowly returns thumbstick
                Vector2 tempThumbStickVal = Vector2.Lerp(currentThumb, toPosition, counter / duration);
                joystickInputDirection = tempThumbStickVal;
                thumbstickTransform.anchoredPosition = tempThumbStickVal;
    
                //Slowly returns the target Object to original pos
                float tempTargetObjAngle = Mathf.Lerp(angle, originalAngle, counter / duration);
                rg2d.MoveRotation(tempTargetObjAngle);
    
                yield return null;
            }
        }
    
        public float returnTime = 1.0f;
        public Rigidbody2D rg2d;
        public bool flipRot = true;
        const float originalAngle = 0;
        bool released = true;
        float angle;
        Coroutine retCoroutine;
    
        void FixedUpdate()
        {
            if (released)
                return;
    
            float horizontal = joystickInputDirection.x;
            float vertical = joystickInputDirection.y;
    
            angle = Mathf.Atan2(horizontal, vertical) * Mathf.Rad2Deg;
            angle = flipRot ? -angle : angle;
    
            rg2d.MoveRotation(angle);
        }
    }