[Solved] Additive Rotation Based on Touch deltaPosition?

Preface: I have searched a bunch and read many answers to questions and threads. None have really helped me complete this properly. I wouldn’t be surprised if its just me being the problem. It is making me feel pretty dumb.

Goal: Using single finger touch, I am trying to make an additive rotation for a game object only that only rotates on the z-axis, based on the difference between the previous touch position and the current touch position.

    public void RotateShape()
    {
        if(Input.touches[0].phase == TouchPhase.Moved)
        {
            Vector3 go_Pos = myCam.WorldToScreenPoint(transform.position);
            Vector2 go_ScreenPos = new Vector2(go_Pos.x, go_Pos.y);

            Vector2 dPos = Input.touches[0].deltaPosition;

            float newAngle = Mathf.Atan2(dPos.y - go_ScreenPos.y, dPos.x - go_ScreenPos.x);

            transform.Rotate(0, 0, newAngle);
        }
    }

(I know I could just make a variable for Input.touches[0], use less variables, etc. I am trying to make it easy to follow)

This particular code block only rotates clockwise. It also doesn’t care about the deltaPosition difference - it pretty much always rotates at the same speed.

I have been stuck on this problem for a while and I haven’t been able to solve it correctly. I have ran into these issues: either the direction only rotates counter-clockwise or clock-wise (never both), jittery (probably just need a threshold), or reverses direction when the mid-point touch is reached. There have been a lot of iterations but to no avail.

I don’t necessarily want someone to write the code for me, I just need someone to help walk me through the proper way to do this as it is a bit frustrating.

I solved my issue. Mathf.Atan2 is useful for 2D not for 3D in this case. I needed to use Vector3.SignedAngle. This applies because I use a perspective camera not an orthographic camera, its a 2D vs 3D problem. I am not going to go through and completely clean up the code but if anyone in the future is reading this: don’t expect this to magically fix your issue but it should help.

NOTE: Every new mouse click (or every new touch[0].Phase == Began [you can use the touch’s phase]) should reset newHitPos to Vector3.zero somewhere for this to work. If you don’t there will be rotation popping.

    public void RotateShape()
    {
        previousHitPos = newHitPos;
        newHitPos = GetPositionFromHit(inputPos);

        float diffAngle = Vector3.SignedAngle(newHitPos, previousHitPos, transform.forward);

        transform.Rotate(0, 0, diffAngle);
    }

    public Vector3 GetPositionFromHit(Vector3 screenPos)
    {
        Ray ray = myCam.ScreenPointToRay(screenPos);

        // hits something and return the relative position
        if (Physics.Raycast(ray, out hit, 100))
        {
            return hit.point - transform.position;
        }

        return newHitPos;
    }

I had also ran into an issue with edge of screen rotation problems. That was fixed on line 19 by adding:
return hit.point - transform.position;

If you properly implement the script it should look something like the follow gif:

2 Likes