- Home /
Physics Based Character Controller Problems With Jitter
I wrote a Physics Based Character controller, everything is working well except I get a jitter that is noticeable when I look while moving. I must be doing both to notice it. The physics updates every 0.02(I think) seconds which is all fine and dandy until I use a camera look script which updates in the update method. With the almost too slow update for the character movement, using the camera at a different interval emphasizes this giving the jitter(that's my guess). I don't want to make physics update more often as that would be unnecessary to have the whole scene be more demanding. I have put the look script in fixed update and no longer noticed a jitter but I know other people could as i may not notice frame rate drops as much. Perhaps the scaling of everything is bad as I understand physics will act differently depending on sizes. I am using the default capsule with a scale of (1, 1, 1) for the temporary player. What i'm looking to do is figure out if I can update the player's physics more often than the rest but I doubt it. So is there any other way to help the situation? Or should I just slap the camera look script in a timer to update as physics do(don't want it directly in fixed update as it executes more than once).
Character Crontroller (Very unfinished):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class NewController : MonoBehaviour
{
//Public Variables
[Range(0, 10)]
public float acceleration = 3.75f;
[Range(0, 10)]
public float deceleration = 1.5f;
[Tooltip("Anybody who can tell me why this is on a different scale than decel gets a cookie cuz im fucken confused...")]
[Range(0, 10)]
public float inAirDrag = 0.15f;
public float minSlope = 0.4f;
public float walkSpeed = 0.7f;
public float runSpeed = 3.9f;
public float jumpForce = 3.75f;
public bool dblJump;
public bool moveInAir;
public bool toggleRun;
public string horizontal = "Horizontal";
public string vertical = "Vertical";
public string jump = "Jump";
public string run = "Run";
public bool debugMode;
[HideInInspector]
public enum MoveState { Run, Walk, Idle, Jump, DiagLeftWalk, DiagRightWalk, DiagLeftRun, DiagRightRun, TurnLeft, TurnRight }
public MoveState moveState { get; private set; }
//Private Variables
private bool isRunning;
private Vector3 localVel;
private Rigidbody rb;
private float decel;
private float accel;
private bool isOnGround;
private float iad;
private bool hasUsedDoubleJump;
void Start()
{
rb = GetComponent<Rigidbody>();
decel = Map(0, 10, 1, 0, deceleration);
accel = Map(0, 10, 0, 50, acceleration);
iad = Map(0, 10, 1, 0, inAirDrag);
}
void Update()
{
#region DeterminRun
if (isOnGround)
{
if (toggleRun)
{
if (Input.GetButtonDown(run))
isRunning = !isRunning;
}
else
{
isRunning = Input.GetButton(run);
}
}
#endregion
#region Movement
//Move
if (moveInAir)
{
rb.AddForce(transform.forward * (Input.GetAxisRaw(vertical) * accel * Time.deltaTime * 200), ForceMode.Acceleration);
rb.AddForce(transform.right * (Input.GetAxisRaw(horizontal) * accel) * Time.deltaTime * 200, ForceMode.Acceleration);
}
else
{
if (isOnGround)
{
rb.AddForce(transform.forward * (Input.GetAxisRaw(vertical) * accel * Time.deltaTime * 200), ForceMode.Acceleration);
rb.AddForce(transform.right * (Input.GetAxisRaw(horizontal) * accel * Time.deltaTime * 200), ForceMode.Acceleration);
}
}
#endregion
#region Control
//Take Local Vel
localVel = transform.InverseTransformDirection(rb.velocity);
if (isOnGround)
{
//Apply Drag X
if (Input.GetAxisRaw(horizontal) == 0)
localVel.x *= decel;
//Apply Drag Z
if (Input.GetAxisRaw(vertical) == 0)
localVel.z *= decel;
}
else
{
//Apply Drag X
localVel.x *= iad;
//Apply Drag Z
localVel.z *= iad;
}
if (isOnGround || moveInAir)
{
//Clamp Z
if (localVel.z > walkSpeed && !isRunning) localVel.z = walkSpeed;
if (localVel.z > runSpeed && isRunning) localVel.z = runSpeed;
if (localVel.z < -walkSpeed && !isRunning) localVel.z = -walkSpeed;
if (localVel.z < -runSpeed && isRunning) localVel.z = -runSpeed;
//Clamp X
if (localVel.x > walkSpeed && !isRunning) localVel.x = walkSpeed;
if (localVel.x > runSpeed && isRunning) localVel.x = runSpeed;
if (localVel.x < -walkSpeed && !isRunning) localVel.x = -walkSpeed;
if (localVel.x < -runSpeed && isRunning) localVel.x = -runSpeed;
}
//Apply Local Vel
rb.velocity = transform.TransformDirection(localVel);
#endregion
#region Jumping
if (Input.GetButtonDown(jump))
{
if (isOnGround)
{
hasUsedDoubleJump = false;
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}
else if (!hasUsedDoubleJump && dblJump)
{
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
hasUsedDoubleJump = true;
}
}
#endregion
}
void OnCollisionExit()
{
isOnGround = false;
}
void OnCollisionStay(Collision collision)
{
foreach (ContactPoint cp in collision.contacts)
{
if (cp.normal.y > minSlope)
{
isOnGround = true;
}
}
}
float Map(float min1, float max1, float min2, float max2, float num)
{
return (num - min1) / (max1 - min1) * (max2 - min2) + min2;
}
}
Look Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewLook : MonoBehaviour
{
//Public Variables
[Header("Looking")]
[Range(0, 30)]
public float sensitivity = 1;
[Range(0, 30)]
public float smoothing = 12;
[Tooltip("Dont touch unless you know this script.")]
[Range(0, 30)]
public float distanceMultiplier = 1;
[Tooltip("The limit of looking Up(U).")]
public float ULimit = -95;
[Tooltip("The limit of looking Down(D).")]
public float DLimit = 95;
public bool cheap = false;
public bool useSmoothing = true;
[Header("Input")]
public string horizontalInput = "MouseX";
public string verticalInput = "MouseX";
[Header("References")]
public Transform player;
//Private Variables
private float smoothingDist;
float smoothFact;
private Vector2 normal;
private Vector2 smooth;
void Start()
{
//Setting Current Values
normal.x = player.localEulerAngles.y;
smooth.x = player.localEulerAngles.y;
normal.y = transform.localEulerAngles.x;
smooth.y = transform.localEulerAngles.x;
}
void Update()
{
//Looking Logic
if (!cheap && useSmoothing)
{
normal.x += Input.GetAxis(horizontalInput) * sensitivity;
normal.y += Input.GetAxis(verticalInput) * sensitivity;
smoothingDist = Vector2.Distance(normal, smooth);
smoothFact = (smoothing * (smoothingDist * distanceMultiplier)) * Time.deltaTime;
smooth = Vector2.MoveTowards(smooth, normal, smoothFact);
}
else if (cheap && useSmoothing)
{
normal.x += Input.GetAxis(horizontalInput) * sensitivity;
normal.y += Input.GetAxis(verticalInput) * sensitivity;
smooth = Vector2.MoveTowards(smooth, normal, smoothing);
}
else
{
smooth.x += Input.GetAxis(horizontalInput) * sensitivity;
smooth.y += Input.GetAxis(verticalInput) * sensitivity;
}
//Look Limits
if (smooth.y < ULimit)
smooth.y = ULimit;
if (smooth.y > DLimit)
smooth.y = DLimit;
//Gotta Do Look Limits For Normal Vector Too, FML
if (normal.y < ULimit)
normal.y = ULimit;
if (normal.y > DLimit)
normal.y = DLimit;
//Applying Stuff
transform.localEulerAngles = new Vector2(smooth.y, 0);
player.localEulerAngles = new Vector2(player.localEulerAngles.x, smooth.x);
}
}
Feel free to try them for yourself and tell me if you can see it.
(Please do not use my code in your games)
"Physics Based" and Update() do not always have a great relationship. Specifically, you are using AddForce() within an Update call. As FixedUpdate() is timed with the Physics engine, Physics operations are typically done there.
It's common to collect user input within the Update() cycle, but those inputs are typically delayed and implemented on the soonest FixedUpdate() call available.
Answer by Lili-Shi · Jun 14, 2017 at 07:01 PM
I have not entirely looked through you scripts, but let me paste in something I wrote in my look-script:
/*
* NOTE: Making a camera LookAt an object, that _IS_ moved using phyics/rigidbodies,
* one should try any and any combinations of these steps to get rid of the weird jitter:
* 1) put the cam LookAt into LateUpdate
* 2) put the cam LookAt into FixedUpdate
* 3) set the rigidbody Interpolate to Interpolation / Extrapolation or even back to None
*/
Basically, if you do anything via the physics system, you better put it into the FixedUpdate/LateUpdate method (not sure which it was), and also fiddle around with the Rigidbody interpolation.
Your answer
![](https://koobas.hobune.stream/wayback/20220612121657im_/https://answers.unity.com/themes/thub/images/avi.jpg)