Hello everyone! I’ve set up my own basic 3rd person controller system from scratch.
The bug I’ve been experiencing is that whenever I move the character in any direction while rotating the camera in any direction, the player is supposed to rotate with the camera smoothly, but lags behind and jitters a lot. I’ve also noticed that the camera movement is also a little janky.
I know that this description is not adequate, so I’ve attached a link to the Unity Build in question just so you can experience it for yourself, if you wish:
I’ve also attached a link to a video of the jitter as well:
THIS IS HOW THE PLAYER AND CAMERA ARE SET UP IN MY UNITY PROJECT:
o CameraManager (Contains the CameraManager script, no other components)
o CameraPivot (Empty game object)
o MainCamera (Camera component)
o Player (Dynamic Rigidbody that is Interpolated and frozen rotation on X, Y, and Z axes and no Gravity, PlayerLocomotion script, Capsule mesh, Capsule Collider)
The CameraManager script:
using UnityEngine;
public class CameraManager : MonoBehaviour
{
[SerializeField] private Transform cameraPivotTransform;
[Header("Camera Values")]
[SerializeField] private float cameraFollowSpeed = 0.2f;
[SerializeField] private float cameraLookSpeed = 2f;
[SerializeField] private float cameraPivotSpeed = 2f;
[SerializeField] private float minimumPivotAngle = -35f;
[SerializeField] private float maximumPivotAngle = 35f;
[SerializeField] private float cameraCollisionRadius = 0.2f;
[SerializeField] private LayerMask collisionLayers; // Layers we want out camera to collide with
[SerializeField] private float cameraCollisionOffset = 0.2f; // How much the camera will jump off of objects its colliding with
[SerializeField] private float minimumCollisionOffset = 0.2f; // How much the camera will jump off of objects its colliding with
private float lookAngle; // Camera looking up and down
private float pivotAngle; // Camera looking left and right
private float defaultPosition;
private float inputX;
private float inputY;
private Transform targetTransform; // The transform the camera will follow
private Transform cameraTransform;
private Vector3 cameraFollowVelocity = Vector3.zero;
private Vector3 cameraVectorPosition;
private void Awake()
{
targetTransform = FindFirstObjectByType<PlayerLocomotion>().transform;
cameraTransform = Camera.main.transform;
defaultPosition = cameraTransform.localPosition.z;
}
private void Update()
{
inputX = PlayerInputListener.instance.lookDirection.x;
inputY = PlayerInputListener.instance.lookDirection.y;
}
private void LateUpdate()
{
FollowTarget();
RotateCamera();
HandleCameraCollisions();
}
private void FollowTarget()
{
Vector3 targetPosition = Vector3.SmoothDamp(transform.position, targetTransform.position, ref cameraFollowVelocity, cameraFollowSpeed * Time.deltaTime);
transform.position = targetPosition;
}
private void RotateCamera()
{
Vector3 rotation;
Quaternion targetRotation;
lookAngle += inputX * cameraLookSpeed * Time.deltaTime;
pivotAngle -= inputY * cameraPivotSpeed * Time.deltaTime;
pivotAngle = Mathf.Clamp(pivotAngle, minimumPivotAngle, maximumPivotAngle);
rotation = Vector3.zero;
rotation.y = lookAngle;
targetRotation = Quaternion.Euler(rotation);
transform.rotation = targetRotation;
rotation = Vector3.zero;
rotation.x = pivotAngle;
targetRotation = Quaternion.Euler(rotation);
cameraPivotTransform.localRotation = targetRotation;
}
private void HandleCameraCollisions()
{
float targetPosition = defaultPosition;
RaycastHit hit;
Vector3 direction = cameraTransform.position - cameraPivotTransform.position;
direction.Normalize();
if (Physics.SphereCast(cameraPivotTransform.position, cameraCollisionRadius, direction, out hit, Mathf.Abs(targetPosition), collisionLayers))
{
float distance = Vector3.Distance(cameraPivotTransform.position, hit.point);
targetPosition = -(distance - cameraCollisionOffset);
}
if (Mathf.Abs(targetPosition) < minimumCollisionOffset)
{
targetPosition -= minimumCollisionOffset;
}
cameraVectorPosition.z = Mathf.Lerp(cameraTransform.localPosition.z, targetPosition, 0.2f);
cameraTransform.localPosition = cameraVectorPosition;
}
}
The PlayerLocomotion script:
using UnityEngine;
public class PlayerLocomotion : MonoBehaviour
{
private Vector3 movementDirection;
private Transform cameraTransform;
private Rigidbody rb;
private float inputX;
private float inputY;
[SerializeField] private float movementSpeed = 7f;
[SerializeField] private float rotationSpeed = 15f;
private void Awake()
{
rb = GetComponent<Rigidbody>();
cameraTransform = Camera.main.transform;
}
private void Update()
{
inputX = PlayerInputListener.instance.movementDirection.x;
inputY = PlayerInputListener.instance.movementDirection.y;
}
private void FixedUpdate()
{
HandleMovement();
HandleRotation();
}
public void HandleMovement()
{
movementDirection = cameraTransform.forward * inputY;
movementDirection += cameraTransform.right * inputX;
movementDirection.y = 0f;
movementDirection.Normalize();
movementDirection *= movementSpeed;
Vector3 movementVelocity = movementDirection;
rb.linearVelocity = movementVelocity;
}
public void HandleRotation()
{
Vector3 targetDirection = Vector3.zero;
targetDirection = cameraTransform.forward * inputY;
targetDirection += cameraTransform.right * inputX;
targetDirection.y = 0f;
targetDirection.Normalize();
if (targetDirection == Vector3.zero)
{
targetDirection = transform.forward;
}
Quaternion targetRotation = Quaternion.LookRotation(targetDirection);
Quaternion playerRotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
rb.MoveRotation(playerRotation);
}
}
OTHER POINTS:
- I made a build for the project, and the problem still persists in the build.
- I know using Cinemachine might be the right thing to do, but I just want to make a basic 3rd person controller from scratch
I would seriously appreciate some help on how to make the player rotate smoothly with the camera! I’m kind of losing my mind I’ve spent way too much time trying to fix this.