- Home /
Player vibrates back and forth when moving
Hello, I've tried creating a new project on Unity3D but I've noticed that whenever I move my player after a while, it starts vibrating back and forth. I've been using a different type of Cinemachine Camera (Virtual Camera, I'll give more details if this can cause the problem) and suddenly that happens. Here's the code that manages Player Movement using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
[Header("Object References")]
public CharacterController controller;
public new Camera camera;
public Animator animator;
[Header("Variables")]
public float walkingSpeed = 5f;
public float sprintSpeed = 10f;
public float turnTime = 0.13f;
private float currentSpeed;
private float turnVelocity;
[Header("Variables (Read-Only)")]
public float verticalSpeed = 0f;
public const float gravityConstant = 9.81f;
public enum State
{
Idle = 0,
Running = 1,
Jumping = 2
}
private void Start()
{
camera = Camera.main;
currentSpeed = walkingSpeed;
}
private void Update()
{
if(Input.GetKeyDown(KeyCode.LeftShift))
{
currentSpeed = sprintSpeed;
}
if(Input.GetKeyUp(KeyCode.LeftShift))
{
currentSpeed = walkingSpeed;
}
}
void FixedUpdate()
{
float horizontal = Input.GetAxisRaw("Horizontal");
float vertical = Input.GetAxisRaw("Vertical");
Vector3 moveDir = new Vector3(horizontal, 0f, vertical).normalized;
/*
* TODO: TRANSFER SPEED, RUNNING AND GRAVITY METHODS/VARIABLES TO
* TEMPLATE FOR OTHER CHARACTERS (ENEMIES AS WELL)
*/
if(moveDir.magnitude >= 0.1)
{
float targetAngle = Mathf.Atan2(moveDir.x, moveDir.z) * Mathf.Rad2Deg + camera.transform.eulerAngles.y;
float finalAngle = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetAngle, ref turnVelocity, turnTime);
transform.rotation = Quaternion.Euler(0f, finalAngle, 0f);
Vector3 finalMoveDir = Quaternion.Euler(0f, targetAngle, 0f).normalized * Vector3.forward;
controller.Move(finalMoveDir * currentSpeed * Time.deltaTime);
animator.SetInteger("State", (int)State.Running);
}
else
{
animator.SetInteger("State", (int)State.Idle);
}
applyGravity();
}
//GRAVITY HANDLER
public void applyGravity()
{
switch(controller.isGrounded)
{
case true:
verticalSpeed = 0f;
break;
case false:
verticalSpeed -= gravityConstant * Time.deltaTime;
break;
}
Vector3 gravityDir = new Vector3(0f, verticalSpeed, 0f);
controller.Move(gravityDir * Time.deltaTime);
}
}
This is the part that actually does the moving:
void FixedUpdate()
{
float horizontal = Input.GetAxisRaw("Horizontal");
float vertical = Input.GetAxisRaw("Vertical");
Vector3 moveDir = new Vector3(horizontal, 0f, vertical).normalized;
/*
* TODO: TRANSFER SPEED, RUNNING AND GRAVITY METHODS/VARIABLES TO
* TEMPLATE FOR OTHER CHARACTERS (ENEMIES AS WELL)
*/
if(moveDir.magnitude >= 0.1)
{
float targetAngle = Mathf.Atan2(moveDir.x, moveDir.z) * Mathf.Rad2Deg + camera.transform.eulerAngles.y;
float finalAngle = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetAngle, ref turnVelocity, turnTime);
transform.rotation = Quaternion.Euler(0f, finalAngle, 0f);
Vector3 finalMoveDir = Quaternion.Euler(0f, targetAngle, 0f).normalized * Vector3.forward;
controller.Move(finalMoveDir * currentSpeed * Time.deltaTime);
animator.SetInteger("State", (int)State.Running);
}
else
{
animator.SetInteger("State", (int)State.Idle);
}
applyGravity();
}
I also found this excellent article regarding stutter caused by timestep issues:
https://www.kinematicsoup.com/news/2016/8/9/rrypp5tkubynjwxhxjzd42s3o034o8
Answer by moatdd · Aug 06, 2020 at 11:30 PM
I think you're calling Controller.Move TWICE in the same fixedupdate frame. Once at Line 64 in FixedUpdate, and again at line 88 in applyGravity().
So a couple of things... first, you might want to combine both of these moves into a single move at the end by taking your finalMoveDir and combining it with the gravityDir from applyGravity, and THEN calling the controller.Move(), because each call of controller.Move() takes collisions into account.
Second, I believe that controller.Move is meant to be applied in the Update() event rather than FixedUpdate() event. (at least most Unity Examples that I've seen indicate this)
Answer by xGoldenK · Aug 07, 2020 at 10:24 AM
I've solved the initial problem about the camera, I've changed the cinemachine camera type to FreeLook instead of Virtual. However, I've tried implementing what you said but now whenever I press the key to jump, the player only jumps "randomly": even if I start the project and completely stand still, if I press space 6 times the player only jump a couple of times. I tried removing the controller.isGrounded and it works (but of course, the player can jump even in mid-air, but at least the rest of the code is working). Here's my current code: using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
[Header("Object References")]
public CharacterController controller;
public new Camera camera;
public Animator animator;
[Header("Variables")]
public float walkingSpeed = 5f;
public float sprintSpeed = 10f;
public float turnTime = 0.13f;
public float jumpForce = 6f;
public State currentState;
private Vector3 finalMoveDir;
//VARIABLES THAT TRACK CURRENT VALUES
private float currentJumpForce = 0f;
private float currentSpeed;
private float turnVelocity;
public enum State
{
Idle = 0,
Running = 1,
Jumping = 2
}
private void Start()
{
camera = Camera.main;
currentSpeed = walkingSpeed;
}
void Update()
{
//HANDLE INPUT
if(Input.GetKeyDown(KeyCode.LeftShift))
{
currentSpeed = sprintSpeed;
}
if(Input.GetKeyUp(KeyCode.LeftShift))
{
currentSpeed = walkingSpeed;
}
if(Input.GetKeyDown(KeyCode.Space) && controller.isGrounded)
{
currentJumpForce = jumpForce;
}
//APPLY FORCES
float horizontal = Input.GetAxisRaw("Horizontal");
float vertical = Input.GetAxisRaw("Vertical");
Vector3 moveDir = new Vector3(horizontal, 0f, vertical).normalized;
Vector3 jumpDir = new Vector3(0f, currentJumpForce, 0f);
if(moveDir.magnitude >= 0.1)
{
float targetAngle = Mathf.Atan2(moveDir.x, moveDir.z) * Mathf.Rad2Deg + camera.transform.eulerAngles.y;
float finalAngle = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetAngle, ref turnVelocity, turnTime);
transform.rotation = Quaternion.Euler(0f, finalAngle, 0f);
finalMoveDir = Quaternion.Euler(0f, targetAngle, 0f).normalized * Vector3.forward;
SetState(State.Running);
}
else
{
switch(currentJumpForce > 0.1f)
{
case true:
SetState(State.Jumping);
break;
default:
SetState(State.Idle);
break;
}
}
controller.Move((finalMoveDir * currentSpeed * Time.deltaTime) + jumpDir);
finalMoveDir = new Vector3(0f, 0f, 0f);
currentJumpForce = 0f;
}
public void SetState(State state)
{
animator.SetInteger("State", (int)state);
currentState = state;
}
}
Your answer
Follow this Question
Related Questions
How to have CharacterController interact with Object 1 Answer
How would I make it so an animation will play, but stop when it hits a collider? 0 Answers
3d animation controller 0 Answers
How to limit the forces applied to an object to just forward, left, and right? 0 Answers
Physics controls with look ahead. 0 Answers