Search code examples
c#unity-game-enginecameratouch

Unity: Camera is jumpy/jerky when using touch grab to move rotation


Using Unity, I'm trying to move my turrets rotation using first person camera by using touch, I grab from side of screen to center from the direction i want to go to like in google maps, the touch grab will move my camera there smoothly.

Problem is the camera being very jerky, not smooth in position.

In Global

private float smoothTouchFactor = 5;

This is the code I am using in Update()

Quaternion rotation;

if (Input.touchCount > 0)
{
    if (Input.GetTouch(0).phase == TouchPhase.Moved)
    {
        Touch touch = Input.GetTouch(0);
        x += touch.deltaPosition.x * xSpeed * 0.02f;
        y -= touch.deltaPosition.y * ySpeed * 0.02f;

        rotation = Quaternion.Euler(y, x, 0f);
        turretHeadToMove.rotation = Quaternion.Slerp(turretHeadToMove.rotation, rotation, Time.deltaTime * smoothTouchFactor);
    }
}

rotation = Quaternion.Euler(y, x, 0f);
turretHeadToMove.rotation = Quaternion.Slerp(turretHeadToMove.rotation, rotation, Time.deltaTime * smoothTouchFactor);

If I dont add the last 2 lines after the if(touchCount) then the camera goes back to initial position after i lift my finger off the screen.


Here is the mouse axis Mouse X/Y code that works and moves the turret with no jerky motion, smoothly.

private float xSpeed = 40.0f;
private float ySpeed = 40.0f;

in Update()
x += Input.GetAxis("Mouse X") * xSpeed * 0.02f;
y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;

Quaternion rotation = Quaternion.Euler(y, x, 0f);
turretHeadToMove.rotation = rotation;

Solution

  • From your description of needing those "two lines" at the end, it sounds like you have code elsewhere that's constantly trying to reset their rotation. So, we add a variable to keep rotation consistent with the Quaternion set the last time the touch control was done, and just be sure to assign it to turretHeadToMove.rotation before handling the touch controls.

    Then, you can use Euler to create a Quaternion of how much you want to rotate this frame. And then multiply the current rotation by that amount in order to get the new rotation

    Altogether this might look like:

    As a field:

    private Quaternion savedRotation = Quaternion.identity;
    

    In Update:

    // Add it before the block so that modifications to `localRotation` are consistent with memory
    turretHeadToMove.rotation = savedRotation;
    
    if (Input.touchCount > 0)
    {
        if (Input.GetTouch(0).phase == TouchPhase.Moved)
        {
            Touch touch = Input.GetTouch(0);
            Quaternion previousRotation = turretHeadToMove.rotation;
    
            Quaternion rotationThisFrame = Quaternion.Euler(
                     touch.deltaPosition.y * ySpeed, 
                     touch.deltaPosition.x * xSpeed, 0f);
    
            turretHeadToMove.rotation = previousRotation * rotationThisFrame;
    
            savedRotation = turretHeadToMove.rotation;
        }
    }