- Home /
Slerp bugs and other issues with turning the player character relative to a third person camera
I'm working on a 3D platformer which uses a third person camera system in which the the camera and player essentially orbit each other, similar to what's used in most 3D Mario and Zelda games (see this video for more detailed explanation of what I'm going for).
This is the code I'm currently using for the camera:
public class PlayerRig : CameraRig
{
public Transform target; //The player character's transform.
public Vector3 lookInput; //The value of the input that controls the (default: Mouse / Right Stick), converted into rotation around the X and Y axes.
public Vector3 offset = new Vector3(0, 1, -4); //The distance of the camera from the target transform when at rest.
// Start is called before the first frame update
void Start()
{
mount = transform.GetChild(0); //The transform the camera is a child of.
}
void OnLook (InputValue value)
{
Vector2 rawInput = value.Get<Vector2>(); //The input recieved from input device.
lookInput = new Vector3(rawInput.y, rawInput.x, 0); //Convert the input into rotation around the X and Y axes.
}
// LateUpdate is called at the end of each frame
void LateUpdate()
{
if (lookInput != Vector3.zero)
{
//Orbit
transform.RotateAround(target.position, target.up, lookInput.y); //Rotate the camera round the player's up axis.
//Elevation
float currentAngle = Vector3.SignedAngle(mount.up, target.up, mount.right); //The current angle between the camera and target transforms aroundthe camera's X axis.
float targetAngle = Mathf.Clamp(currentAngle + lookInput.x, -80, 25); //The clamped sum of currentAngle and lookInput.x, prevents the camera from moving directly above or below the player.
transform.RotateAround(target.position, mount.right, currentAngle - targetAngle); //Rotate the camera vertically around the player.
}
Vector3 destination = target.position + ((transform.position - target.position).normalized * offset.magnitude); //Get the position the camera should move towards.
transform.position = Vector3.Lerp(transform.position, destination, Time.deltaTime * 1.5f); //Move the camera towards its destination.
if ((transform.position - target.position).magnitude > 5)
{
transform.position = target.position + ((transform.position - target.position).normalized * 5); //Prevent the camera from getting too far from the player.
}
if ((transform.position - target.position).magnitude < 3.5f)
{
transform.position = target.position + ((transform.position - target.position).normalized * 3.5f); //Prevent the camera from getting too close to the player.
}
//Make camera face target
mount.LookAt(target); //Turn the camera to face the player.
}
}
And this is the relevant code from my player controller:
public class PlayerController : MonoBehaviour
{
//Components
[SerializeField] Camera gameCamera; //The main gameplay camera.
Rigidbody playerbody; //The Rigidbody associated with the player character.
//Physics Constants
private float acc = 0.084375f; //Aceleration rate on the ground (m/s).
private float dec = 0.9f; //Deceleration rate when input direction is opposite the current velocity on the ground (m/s).
private float top = 10.8f; //The maximum speed the character can reach under their own power on flat ground or in the air (m/s).
//Flags
public bool isGrounded = true; //Whether or not the player is on the ground.
//Inputs
[SerializeField] Vector3 moveInput; //The value of the "Move" InputAction (default: Left Stick, WASD) converted into Vector3 (value.x, 0, value.y).
// Start is called before the first frame update.
void Start()
{
playerbody = GetComponent<Rigidbody>(); //Get the Rigidbody on this GameObject.
}
//On Move is called when activity from the Move InputAction is detected.
public void OnMove(InputValue value)
{
Vector2 rawInput = value.Get<Vector2>();
moveInput = new Vector3(rawInput.x, 0, rawInput.y);
}
// FixedUpdate is called once per fixed timestep frame.
void FixedUpdate()
{
if (moveInput != Vector3.zero)
{
if (isGrounded == true)
{
Accelerate(acc);
}
}
}
void Accelerate(float acc)
{
//Get direction of input relative to the camera direction.
Vector3 localizedInput = Vector3.Normalize((Quaternion.FromToRotation(Vector3.forward, Vector3.ProjectOnPlane(gameCamera.transform.forward, transform.up).normalized) * moveInput));
//Turn the player in the direction of input relative to the camera direction.
transform.rotation = Quaternion.Slerp(Quaternion.LookRotation(transform.forward, transform.up), Quaternion.LookRotation(localizedInput, transform.up), Time.fixedDeltaTime * 6);
playerbody.velocity = transform.forward * playerbody.velocity.magnitude;
//Check if the player's current speed is less than their normal top speed. Otherwise their current speed should be maintained.
if (playerbody.velocity.magnitude < top)
{
//If the sum of the players current speed and the acceleration rate would not exceed the normal top speed,
//add acceleration force to the current velocity.
if (playerbody.velocity.magnitude + acc < top)
{
playerbody.AddForce(acc * localizedInput, ForceMode.VelocityChange);
}
//If the sum of the player's current speed and the acceleration rate would exceed their normal top speed,
//add the difference between current top speed and current speed to current velocity.
else
{
playerbody.AddForce((top - playerbody.velocity.magnitude) * localizedInput, ForceMode.VelocityChange);
}
}
}
There are two main problems I'm having, both occur when holding right or left on the movement input and both seem to be related to the character being rotated by Quaternion.Slerp as neither occurs when I change the code to make the character instantly face the desired direction. The first problem is the player character jittering as it turns. I believe this is a consequence of making the character Slerp towards a moving target which it can never quite reach, the camera being rotated in LateUpdate while the character is rotated in FixedUpdate may also be a factor, although moving the turning code into Update did not in itself fix the problem. In any case but I am unsure of how to resolve the issue. The second and more pressing problem is that, while turning the character occasionally snaps to face the camera's forward vector for a frame before returning to its previous position. I don't have any idea what is causing this behavior, so any help is appreciated.
On a semi-related note: With my current code, using the mouse to control the camera while the player character is in motion is extremely rough. I have a few ideas on how to fix this myself, but I'm open to any suggestions you may have.
Your answer
Follow this Question
Related Questions
Spherical Movement follow Camera jittery movement. 0 Answers
Newbie: ThirdPersonCamera Issue 0 Answers
Why does my camera stop following the player when I add a premade map? 0 Answers
Script only works if i enable/disable it in inspector 0 Answers
Unity automatically downloading Toolchain Win Linux x64 when opening settings 0 Answers