Weird Stuttering With Camera [SOLVED]

Hi!

I made a post about this problem of mine before, but got no answers. :frowning:

So I decided to try my best to dig into the problem with the help of Google and I think I’ve made some significant progress! :smile:

I’ve simplified my camera rotation code and have made the hierarchy much simpler than it was before. (Which was probably scaring off people from my previous post. :face_with_spiral_eyes:)

The just of my system includes an object and camera. These objects are NOT parented in any way:

The object has a script attached that creates player movement (I feel that this script may not be the problem, but I included it anyways just in case):
3579254--289208--upload_2018-7-28_14-16-18.png

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour {

    static Animator anim;

    public float speed = 1.07f;
    public float runMultiplier = 1.5f;
    public float jumpHeight = 0.91f;
    public float distanceFromGroundUntilNextJump = 0.28f;
    public float maxSpeed = 1f;
    public Rigidbody rigidBody;

    enum Speed { Slow, Regular };
    Speed playerSpeed = Speed.Regular;

    float varyingMaxSpeed;
    float varyingSpeed;

    bool running = false;
    bool isGrounded = false;
    bool wasHoldingShift = false;

    float animX;
    float animY;

    void Start () {

        // I made the player visual invisible and the player controller visible for the sake of simplicity.
        anim = GameObject.Find("PlayerVisual").GetComponent<Animator>();

        varyingMaxSpeed = maxSpeed;
        varyingSpeed = speed;

    }


    void FixedUpdate()
    {
        Animation();
        CheckRunning();
        Movement();
        checkGround();
    }

    void Animation()
    {
        if (running)
        {
            animX = Input.GetAxis("Horizontal4Anim");
            animY = Input.GetAxis("Vertical4Anim");
        }
        else
        {
            animX = Input.GetAxis("Horizontal4Anim") * 0.5f;
            animY = Input.GetAxis("Vertical4Anim") * 0.5f;
        }

        anim.SetFloat("MoveX", animX, 0.1f, Time.deltaTime);
        anim.SetFloat("MoveY", animY, 0.1f, Time.deltaTime);
    }

    void Movement()
    {

        if (!keyDown())
        {
            varyingMaxSpeed = 0.0f;
        }

        switch (playerSpeed)
        {
            case Speed.Regular:

                rigidBody.AddRelativeForce(new Vector3(Input.GetAxis("Horizontal") * varyingSpeed, 0.0f, Input.GetAxis("Vertical") * varyingSpeed) * Time.deltaTime);

                if (running)
                {
                    varyingMaxSpeed = maxSpeed * runMultiplier;
                    varyingSpeed = speed * runMultiplier;
                }
                else
                {
                    varyingMaxSpeed = maxSpeed;
                    varyingSpeed = speed;
                }

                break;
            case Speed.Slow:

                rigidBody.AddRelativeForce(new Vector3(Input.GetAxis("Horizontal") * varyingSpeed * 0.17f, 0.0f, Input.GetAxis("Vertical") * varyingSpeed * 0.17f) * Time.deltaTime);

                if (running)
                {
                    varyingMaxSpeed = maxSpeed * runMultiplier;
                    varyingSpeed = speed * runMultiplier;
                }
                else
                {
                    varyingMaxSpeed = maxSpeed;
                    varyingSpeed = speed;
                }

                break;
        }

        float yCache = rigidBody.velocity.y;
        rigidBody.velocity = new Vector3(rigidBody.velocity.x, 0.0f, rigidBody.velocity.z);
        rigidBody.velocity = Vector3.ClampMagnitude(rigidBody.velocity, varyingMaxSpeed);
        rigidBody.velocity = new Vector3(rigidBody.velocity.x, yCache, rigidBody.velocity.z);

        if (isGrounded == true)
        {

            if (!keyDown())
            {
                float curSpeed = rigidBody.velocity.magnitude;
                float newSpeed = curSpeed - 53f * Time.deltaTime;
                if(newSpeed < 0.0f)
                {
                    newSpeed = 0.0f;
                }

                rigidBody.velocity = rigidBody.velocity.normalized * newSpeed;
            }

            playerSpeed = Speed.Regular;

            if (Input.GetButton("Jump"))
            {

                anim.SetBool("Jumping", true);
                rigidBody.velocity = new Vector3(rigidBody.velocity.x, 0.0f, rigidBody.velocity.z);
                rigidBody.AddForce(0.0f, jumpHeight / 4, 0.0f);

                if (!keyDown())
                {
                    rigidBody.velocity = new Vector3(rigidBody.velocity.x * 0.7f, rigidBody.velocity.y, rigidBody.velocity.z * 0.7f);

                }
            }
        }
        else
        {
            playerSpeed = Speed.Slow;
        }
    }

    void checkGround()
    {
        if (Physics.Raycast(gameObject.transform.position, Vector3.down, distanceFromGroundUntilNextJump))
        {

            rigidBody.velocity = new Vector3(rigidBody.velocity.x, 0.0f, rigidBody.velocity.z);

            isGrounded = true;

            if (anim.GetBool("Jumping"))
            {
                anim.SetBool("Jumping", false);
            }

        }
        else
        { 

            isGrounded = false;
        }
    }

    bool keyDown()
    {
        return (Input.GetAxis("Vertical") > 0.2f || Input.GetAxis("Vertical") < -0.2f || Input.GetAxis("Horizontal") > 0.2f || Input.GetAxis("Horizontal") < -0.2f);
    }

    void CheckRunning()
    {

        if (Input.GetButton("RunCont"))
        {
            running = true;
        }

        if (Input.GetButton("Run"))
        {
            running = true;
            wasHoldingShift = true;

        }
        else if (!Input.GetButton("Run") && wasHoldingShift)
        {
            running = false;
            wasHoldingShift = false;

        }
    }
}

The camera has a script attached that deals with the camera’s movement (This is where I believe the problem lies.):
3579254--289209--upload_2018-7-28_14-20-21.png

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TPC : MonoBehaviour {

    //The object that the camera will be looking at. (The Player.)
    public Transform lookAt;

    //The minimum distance that the camera can go. (For camera collisions.)
    public float minimumDistance = 1.0f;

    //The maximum distance that the camera can go. (For camera collisions.)
    public float maximumDistance = 5.0f;

    //How smooth the transition is between distances.
    public float distanceSmoothing = 10.0f;

    //How far the player can rotate the camera up.
    public float minimumAngle = -80.0f;

    //How far the player can rotate the camera down.
    public float maximumAngle = 80.0f;

    //How much your mouse movement influences
    public float sensitivity = 1.0f;

    //Determines whether the mouse controls return opposite values.
    public bool invertedLook = false;

    //The paramater that will determine the final distance value for the camera.
    private float distance = 0.0f;

    //Returns the updated calculated distance value. (I have 2 values for distance in order to add a smoothing function.)
    private float newDistance = 0.0f;

    //The mouse positions on the x and y axes.
    private float currentX = 0.0f;
    private float currentY = 0.0f;

    private void Start()
    {

        //Makes sure that the cursor doesn't move.
        Cursor.lockState = CursorLockMode.Locked;

        //Makes sure the cursor is invisible.
        Cursor.visible = false;

    }

    private void Update()
    {

        //Inverted Mouse mode on/off.
        if (invertedLook)
        {
            currentX += -Input.GetAxis("Mouse X") * sensitivity;
            currentY += Input.GetAxis("Mouse Y") * sensitivity;
        } else
        {
            currentX += Input.GetAxis("Mouse X") * sensitivity;
            currentY += -Input.GetAxis("Mouse Y") * sensitivity;
        }

        //Limit how much the player can see up and down.
        currentY = Mathf.Clamp(currentY, minimumAngle, maximumAngle);
    }

    private void LateUpdate()
    {
        //Make a rotation value corresponding to the mouse movement that will be used for a couple calculations.
        Quaternion rotation = Quaternion.Euler(currentY, currentX, 0);

        //Gets the position of the maximum distance that the camera can go from the player.
        Vector3 maxDisCheck = new Vector3(0, 0, -maximumDistance);
        Vector3 desiredCameraPos = lookAt.position + rotation * maxDisCheck;

        RaycastHit hit;

        //Makes a raycast between the camera's current position and the farthest position.
        if (Physics.Linecast(lookAt.position, desiredCameraPos, out hit))
         {
            //If there's something in between those points, set the new distance to the distance between the player and where the raycast hit.
             newDistance = Mathf.Clamp(hit.distance * 0.6f, minimumDistance, maximumDistance);
         }
         else
        {
            // If there isn't, then just set the new distance to as far as it can go, because theres nothing stopping it from going that far.
            newDistance = maximumDistance;
        }

        //Make the real distance variable equal to the new distance variable, but with a lerp to make it a smooth transition.
        distance = Mathf.Lerp(distance, newDistance, Time.deltaTime * distanceSmoothing);

        //Set the position of the camera much similar to the desiredCameraPos variable, but instead of using the maximum distance, use the calculated distance variable.
        Vector3 direction = new Vector3(0, 0, -distance);
        transform.position = lookAt.position + rotation * direction;
        transform.LookAt(lookAt.position);

        //Allow the "MouseX" value to determine the y rotation of the player.
        lookAt.transform.rotation = Quaternion.Euler(lookAt.transform.rotation.x, currentX, lookAt.transform.rotation.z);

    }
}

I cannot provide a video of whats happening because the frame rate of the video would have to be EXACTLY the same as the game to see the effect. (Which is impossible for me.)

The camera is sort of stuttering when it moves without turning the camera, but is most noticeable when I circle objects.

I’ve been on this problem for about half a month now and it really puts a roadblock in my game’s development. If anyone has the slightest idea of whats going on, ANY help will be much appreciated! :smile:

Thank you! :smile:

I run your camera script and see no issue.
Did you run your camera script in editor?
Try create new game window, outside main unity work space. Separate it.
And try build a game, to see, if the stuttering exists.
Editor itself can create some lag spikes.

I built and ran the game and got the same results.

I’ve realized throughout the many tests that I conducted that the root of the problem exists not within the camera script, but how the camera and the player interact. If I control the movement of the player with transform.Translate(), the shakiness is gone, but when I control it with rigidBody.AddForce() or rigidBody.AddRelativeForce(), the shakiness appears. Throughout all of this, the camera’s script remains the same. I’ve seen many problems with using transform.Translate() to move characters, so that’s not an option for me. I’ve also tried changing things with interpolation and continuous, discrete, and continuous dynamic collision detection, and unless there’s a specific combination of those settings, (which I think I’ve tried them all) that doesn’t work either. :face_with_spiral_eyes:

Try put all physics movement on the same update Unity clock.
Movements and physics should be on FixedUpdate.

I’ve done that, they’re all called in the FixedUpdate method, they’re just put into “sections” (Different methods) for organization purposes, unless you were talking about the camera movement too, I’ve done a test on that and I get the same results.

P.S. I have tried putting both the movement and the camera controls in FixedUpdate, and it does technically “remove the stuttering”, but by spreading the stuttering out by emulating reduced frame rate. (The game looks as if its running at 30 fps all the time.) It was a solution with a nasty price, so I avoided it.

Have you looked carefully in the editor, what is actually shaking? camera or object (s)?
Because if camera is in separate script, it should not be affected. Unless is on wring frequency.

By “stutter” are you implying that the camera looks jittery or that it is doing something else? I noticed that you are lerping a distance to move the camera, have you considered simply using SmoothDamp() instead? In my experience that gives rather clean camera motion.

I sincerely apologize for the late response. I was studding for my final that I just got done today. (Thank god. :face_with_spiral_eyes:)

I think I made the mistake of not attaching the actual project file so that you can see what I mean.

Here it is:
OSOC.zip

By the way I integrated Timato0’s idea of using SmoothDamp(). Unfortunately it didn’t get rid of the stutter, but I have seen videos say in fact that using Lerp() to move cameras can be unstable. So it did fix something and if not, it fixed a problem that I would most likely run into in the future. Thank you for that! :smile:

3585121–290083–OSOC.zip (1.77 MB)

Bump

I’m still trying to figure this out! Please help someone. :frowning:

Hello, try using this free feature that I did.

Probably the problem is in the ‘lookAt’ command. I’ve had similar problems using LookAt. The solution was to create my own method for doing LookAt.

I still get the same shaking problem, even when I moved my character with the simplest AddRelativeForce code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerTEST : MonoBehaviour {

    Rigidbody rigidBody;
    public float speed = 0.0f;

    // Use this for initialization
    void Start () {

        rigidBody = gameObject.GetComponent<Rigidbody>();
    }
   
    // Update is called once per frame
    void FixedUpdate () {


        rigidBody.AddRelativeForce(new Vector3(Input.GetAxis("Horizontal") * speed, 0.0f, Input.GetAxis("Vertical") * speed));
        rigidBody.AddRelativeForce(new Vector3(Input.GetAxis("Horizontal") * speed, 0.0f, Input.GetAxis("Vertical") * speed));
    }
}

Bump

I made a REALLY REALLY REALLY simplified version of my problem in a new project in hopes that someone can help me. Here it is:

Camera Stutter Issue.zip

3636550–296677–Camera Stutter Issue.zip (401 KB)

Try replace
void LateUpdate ()
with
void FixedUpdate ()

and see if any better

I finally figured it out.

I went to Edit > Project Settings > Time and changed the “Fixed Timestep” from 0.02 to 0.005

Apparently I’ve been experiencing a bug that Unity hasn’t squashed yet. ¯_(ツ)_/¯

Other than that thank you all for your attempts, I really appreciate it!

Cheers!

8 Likes

You should read upon implication of.
Increasing FixedTime step frequency.
You are likely will run soon int major performance hit, once adding more objects.

Did you try my suggestion?

1 Like

Yes, unfortunately I’ve tried that before with no luck as well as with every other update method.

Im having difficulty in unity 2018.3
this version has lots of bugs, the movement is stuttering having your camera followed the gameobject your camera will stutter too, if you dont use Vector3.SmoothDamp

I tried many ways, time.deltatime, time.fixeddeltatime, translate, all of that stutter, in the update and in fixedupdate.

and the new prefab system consumes you lots of clicks and entering inside a prefab is a waste of time.

1 Like

Reducing the physics timestep is not the recommended way to solve this issue, nor is moving your camera movement code into FixedUpdate. This is a common problem that occurs with all computer graphics due to the instantaneous and discrete nature of rendering. You can get more detailed information from this site Timesteps and Achieving Smooth Motion in Unity — KinematicSoup Technologies Inc.

The end solution is to use some kind of interpolation on both the camera and the graphical representation of the player. The camera itself needs to be interpolated so that the slightly jerky motion of the player character get smoothed out. On top of that you need to separate the rendered object that represents the player (the avatar as I’ll refer to it) from the physical one that the player is controlling (the controller as I’ll refer to it). The avatar object also should be interpolated as it needs to be moving smoothly rather than with the slightly jerky fixed motion that the physics simulation will have. I suppose an alternative would be to simply smooth the avatar object and then have the camera follow that in LateUpdate(). Haven’t tried it that way myself but I don’t see why it wouldn’t work. The tricky part will be how you handle interpolating the avatar. If you want to keep it parented to the controller object you’ll need to take that into account, otherwise you could have it as a separate object that smoothly follows the controller instead

4 Likes

Had the same problem and I fixed it by just decreasing the Clipping planes from the Camera. Hope it helps! Two years after lol

2 Likes

Oh. My. God.

I have been slamming my head against my desk trying to figure out this exact same issue for way too long. I feel like I must have googled every combination of keywords possible. THIS fixed it for me. I dropped the far clipping plane from 1000 to 800 and BANG. Right there.

BastingQuill, thank you so much.