I started learning to use Unity and C# scripting a few weeks ago and I have a hard time achieving smooth movement for my player object in a 3D environment using physics.
I am using 3 game objects (player, camera, focal point) and 2 scripts (player movement and camera movement). My main camera is a child object of a focal point empty object. The camera movement script is attached to the focal point object. My player and focal point objects use rigidbodies with interpolation.
My goal is to have a 3rd person camera following an object that can move freely on the x and z axis ( + space bar for additional impulse), and rotates on the y axis based on focal point/camera rotation.
This is my code for the focal point/camera movement:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveCamera : MonoBehaviour
{
private GameObject player;
private Rigidbody camRb;
private float speed;
private void Start()
{
// Assign references/variable
player = GameObject.Find("Player");
camRb = GetComponent<Rigidbody>();
speed = 200;
}
void FixedUpdate()
{
RotateCamera();
}
private void LateUpdate()
{
FollowPlayer();
}
void RotateCamera()
{
// Get horizontal axis and rotate on input
float horizontalInput = Input.GetAxis("Horizontal");
Quaternion targetRot = Quaternion.AngleAxis(horizontalInput * speed * Time.deltaTime, Vector3.up);
camRb.rotation *= targetRot;
// or transform.Rotate(Vector3.up, horizontalInput * speed * Time.deltaTime); ?
}
void FollowPlayer()
{
Vector3 velocity = Vector3.zero;
transform.position = Vector3.SmoothDamp(transform.position, player.transform.position, ref velocity, 0.02f);
}
}
And this is my code for the player movement:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
private Rigidbody playerRb;
private GameObject focalPoint;
private float speed;
private float boostStrength;
private bool boostInput;
void Start()
{
// Assign references/variables
playerRb = GetComponent<Rigidbody>();
focalPoint = GameObject.Find("Focal Point");
speed = 100;
boostStrength = 100;
}
void Update()
{
// Check for boost input
if (Input.GetKeyDown(KeyCode.Space))
{
boostInput = true;
}
}
void FixedUpdate()
{
ForwardMovement();
}
void ForwardMovement()
{
// Move towards focal point on vertical input
float verticalInput = Input.GetAxis("Vertical");
playerRb.AddForce(focalPoint.transform.forward * verticalInput * speed);
// Temporary boost on space bar
if (boostInput)
{
playerRb.AddForce(focalPoint.transform.forward * verticalInput * boostStrength, ForceMode.Impulse);
boostInput = false;
}
// Smooth rotation based on camera rotation and horizontal input - THIS IS WHERE THINGS DON'T GO AS EXPECTED
float horizontalInput = Input.GetAxis("Horizontal");
float velocity = 0.0f;
float targetRotation = Mathf.Atan2(horizontalInput / 3, verticalInput) * Mathf.Rad2Deg + focalPoint.transform.eulerAngles.y;
targetRotation = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref velocity, 0.02f);
Quaternion targetRot = Quaternion.AngleAxis(targetRotation, Vector3.up);
playerRb.MoveRotation(targetRot);
}
For simplicity’s sake, let’s assume that last part was something like this instead:
// Rotation based on camera rotation
float cameraY = focalPoint.transform.rotation.eulerAngles.y;
transform.localRotation = Quaternion.Euler(0, cameraY, 0);
}
These last two lines of codes are what I was using when I noticed that although the player’s forward motion was smooth, the rotation motion (which is based on focal point/camera rotation) stuttered.
I tried to find solutions to this issue by reading the scripting API and checking out forum threads, and learned a lot. I tried several bits of code that I thought would help smooth out movement, including rigidbody.MoveRotation, Slerp, SmoothDamp, RotateTowards, FromToRotation, LookRotation, etc.
These gave varied and interesting results, but everything I tried had the same stuttering effect to some extent.
I then read about execution order, timesteps and the differences between Update(), FixedUpdate() and LateUpdate(). I tried to apply what I learned in my scripts by using Update() to detect user input, LateUpdate() for the FollowPlayer() camera function, and FixedUpdate() for the actual movement.
However, this didn’t solve my problem. To be clearer, everything runs smoothly, except the rotation of the player game object.
Now, I changed the fixed timestep from 0.02 to 0.01, and all motion is smooth (enough). However, I read that changing the fixed timestep was not usually recommended, as it could affect performance.
So, here comes my actual question: are there ways to achieve smooth motion without changing the fixed timestep? how do more experienced coders deal with such issues?
I’m realising now that this post is rather long, so thank you so much for taking the time to read it up to this point!
(Also, this is my first time posting and english is not my mother tongue, so don’t hesitate to mention incorrect format/language)