0
\$\begingroup\$

In my game, I have a third person character. The player moves with a rigidbody and has isKinematic set off with interpolate on. In the script, it moves with Rigidbody.MovePosition with the y position froze. Here's my player script:

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

public class PlayerController : MonoBehaviour
{
   public float speed;

   public Rigidbody rb;

   public Transform camPos;

   // Start is called before the first frame update
   void Start()
   {
       rb = GetComponent<Rigidbody>();
       camPos = Camera.main.transform;
   }

   private void FixedUpdate()
   {
       if (Input.GetKey("w"))
       {
           rb.MovePosition(transform.position + (camPos.forward * speed * Time.fixedDeltaTime));
       }                                          
       if (Input.GetKey("s"))
       {
           rb.MovePosition(transform.position + (-camPos.forward * speed * Time.fixedDeltaTime));
       }
       if (Input.GetKey("d"))
       {
           rb.MovePosition(transform.position + (camPos.right * speed * Time.fixedDeltaTime));
       }
       if (Input.GetKey("a"))
       {
           rb.MovePosition(transform.position + (-camPos.right * speed * Time.fixedDeltaTime));
       }

       rb.velocity = Vector3.zero;
   }
}

For my camera, I have it lookat the player and lag behind smoothly. Here's that script:

//Hakeem Thomas
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CamPos : MonoBehaviour
{
    public Transform target;
    public float smoothTime = 0.5F;
    private Vector3 velocity = Vector3.zero;

    public GameObject player;

    private float mouseX, mouseY;
    public int mouseX_Speed, mouseY_Speed;
    
    //mouseSensitivity
    public int turnSpeed;

    void getMouseXY()
    {
        mouseX += Input.GetAxis("Mouse X") * smoothTime;
        mouseY -= Input.GetAxis("Mouse Y") * smoothTime;
    }
    void LateUpdate()
    {
        getMouseXY();
        // Define a target position above and behind the target transform
        Vector3 targetPosition = target.TransformPoint(new Vector3(0, 1.5f, -2.5f));

        // Smoothly move the camera towards that target position
        transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref velocity, smoothTime);
        transform.LookAt(target);

        target.rotation = Quaternion.Euler((mouseY * mouseY_Speed), (mouseX * mouseX_Speed), 0);
        player.transform.rotation = Quaternion.Euler(0, (mouseX * turnSpeed), 0);

    }
}

My issue is that whenever the player moves either the camera/player jitters.

I have debugged the player transform y position to make sure it wasn't moving up or down (it doesn't). To help with this issue I have either turned down the Fixed Timestep under Time in the project settings or put the code in the camera script is fixed update instead of late update (which is an issue based on my research).

I know MovePosition for the player isn't the right way to move, but I don't know how to make the player move in the direction the camera is facing with rb.velocity. I suspect the issue may lay in the player script with variable camPos. Either way, I would like to stop the jittering.

\$\endgroup\$
0
\$\begingroup\$

I made this mistake too:

target.rotation = Quaternion.Euler((mouseY * mouseY_Speed), (mouseX * mouseX_Speed), 0);
player.transform.rotation = Quaternion.Euler(0, (mouseX * turnSpeed), 0);

When you write to any part of the transform - including rotation - you disable rigidbody interpolation for that frame. The physics engine is bypassed, so it treats it as though the object has teleported, and tries to resume physics-driven, interpolated movement next frame.

If your object has a rigidbody, ensure you're routing all changes to its transformation, both position and rotation, through that body. That way the physics engine remains in control and you get the full benefit of its interpolation.

You're already using MovePosition() instead of transform.position = ..., so you should use MoveRotation() instead of transform.rotation = ....

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks, not just for the answer but the explanation was invaluable. So, I basically should just replace most of my manual code with standard unity library stuff? \$\endgroup\$ – Hakeem Thomas Jul 30 at 17:57
  • \$\begingroup\$ Just replacing transform.rotation = with MoveRotation should suffice for fixing this problem. \$\endgroup\$ – DMGregory Jul 30 at 17:58
  • \$\begingroup\$ One problem with this solution is that my camera doesn't move with a rigidbody. The camera script is attached to the camera. How would I use MoveRotation? \$\endgroup\$ – Hakeem Thomas Jul 30 at 18:08
  • \$\begingroup\$ To quote the answer above: "If your object has a rigidbody, ensure you're routing all changes to its transformation, both position and rotation, through that body." - If your object does not have a rigidbody, this advice does not apply, and it is OK to write to the transform directly, with transform.rotation =, transform.Rotate() etc. \$\endgroup\$ – DMGregory Jul 30 at 18:15
  • 1
    \$\begingroup\$ That is one solution for judder on the character, but it's one that would often be undesirable, since it means your camera movement is less smooth. Every so often you'll show two frames in a row with the camera in the same position, because no FixedUpdate occurred in-between to move it. Using rigidbody interpolation effectively can avoid this issue. \$\endgroup\$ – DMGregory Jul 30 at 18:48
0
\$\begingroup\$

Another common cause of jitter is combining movement code in Update() with movement code in FixedUpdate().

The default physics step is 0.02 seconds, which is 50 updates per second. That's slightly less than the typical monitor refresh rate of 60fps. This means every tenth of a second, you can expect an update sequence that looks sort of like this:

FixedUpdate
Update
FixedUpdate
Update
FixedUpdate
Update
FixedUpdate
Update
Update   <--------
FixedUpdate
Update

Because we have 5 physics updates for every 6 screen updates, about 10 times a second we're going to have a frame where the camera moves but the target didn't move. This causes very noticeable jitter.

There are two easy solutions, each which has its own downsides:

  1. Reduce the physics update timestep (increase the number of physics updates per second). This increases the performance cost of running physics, and may not help on devices where the monitor refresh rate is much higher (e.g. on a 120hz or 240hz monitor).
  2. Move the camera during FixedUpdate. Users with high-refresh-rate monitors may be able to tell that the camera motion isn't as smooth as it should be (since the camera is moving at 50hz but rendering at 120/240hz), but at least it shouldn't jitter.

I just made a simple test project to reproduce your issue. I saw major jitter when moving the camera in Update()/LateUpdate(), and no jitter when moving the camera in FixedUpdate().

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Putting the code in Fixedupdate would solve my problems but I don't want more issues down the road. It seems like a lose-lose situation. \$\endgroup\$ – Hakeem Thomas Jul 30 at 19:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.