Hi, I’m working on a 3D third person perspective game, and I want the player object to rotate as it is moved in various directions. I have accomplished this goal however, the rotation is not smooth. I want the rotation to gradually turn the player in the direction it is moving; not instantly turn it that way.
Here’s the code I have for it so far:
public float playerAcceleration_float = 50f;
public float playerMaxSpeed_float = 20f;
public float playerJumpStrength_float = 500f;
private float playerXSpeed_float;
private float playerZSpeed_float;
private Vector3 playerVelocityAxis_Vector3;
private Camera thirdPerson_Camera;
void Start()
{
thirdPerson_Camera = GameObject.FindGameObjectWithTag("MainCamera").GetComponent<Camera>() as Camera;
}
private void FixedUpdate()
{
playerXSpeed_float = Input.GetAxis("Horizontal");
playerZSpeed_float = Input.GetAxis("Vertical");
playerVelocityAxis_Vector3 = new Vector3(playerXSpeed_float, 0f, playerZSpeed_float);
playerVelocityAxis_Vector3 = Quaternion.AngleAxis(thirdPerson_Camera.transform.eulerAngles.y, Vector3.up) * playerVelocityAxis_Vector3;
if (playerVelocityAxis_Vector3.magnitude > 0)
{
transform.rotation = Quaternion.LookRotation(playerVelocityAxis_Vector3);
}
GetComponent<Rigidbody>().AddForce(playerVelocityAxis_Vector3.normalized * playerAcceleration_float);
transform.rotation = Quaternion.Euler(0f, transform.eulerAngles.y, 0f);
...
Well, you need to Quaternion.Slerp the angle. You would probably need to do that within fixed update so you would need a start rotation.
bool rotating = false;
float speed= .1f;
Quaternion startRotation;
if (playerVelocityAxis_Vector3.magnitude > 0 )
{
if(!rotating){
startRotation = transform.rotation;
rotating = true;
}
if (rotating && transform.rotation != Quaternion.LookRotation(playerVelocityAxis_Vector3){
transform.rotation = Quaternion.Slerp(startRotation,Quaternion.LookRotation(playerVelocityAxis_Vector3),speed * Time.time);
}
else rotating = false;
}
That’s just putting down some code I think would work. You can look up Quaternion.Slerp in the Scripting API
I tried implementing your code there, and it works in place of what I had for rotating the character while one direction is pressed and a perpendicular direction is pressed before releasing the first direction.
This much my code could do before, and the problem still remains.
When no direction is being pressed, and then a direction is pressed, the player object’s rotation is immediately snapped towards the directional input.
private void FixedUpdate()
{
playerXSpeed_float = Input.GetAxis("Horizontal");
playerZSpeed_float = Input.GetAxis("Vertical");
playerVelocityAxis_Vector3 = new Vector3(playerXSpeed_float, 0f, playerZSpeed_float);
playerVelocityAxis_Vector3 = Quaternion.AngleAxis(thirdPerson_Camera.transform.eulerAngles.y, Vector3.up) * playerVelocityAxis_Vector3;
if (playerVelocityAxis_Vector3.magnitude > 0)
{
if (!rotating_bool)
{
startRotation_Quaternion = transform.rotation;
rotating_bool = true;
}
if (rotating_bool && transform.rotation != Quaternion.LookRotation(playerVelocityAxis_Vector3))
{
transform.rotation = Quaternion.Slerp(startRotation_Quaternion, Quaternion.LookRotation(playerVelocityAxis_Vector3), turnSpeed_float * Time.time);
}
else
{
rotating_bool = false;
}
}
GetComponent<Rigidbody>().AddForce(playerVelocityAxis_Vector3.normalized * playerAcceleration_float);
transform.rotation = Quaternion.Euler(0f, transform.eulerAngles.y, 0f);
...
What is this line supposed to do, because it’s interfering with rotation?
transform.rotation = Quaternion.Euler(0f, transform.eulerAngles.y, 0f);
It’s hard to tell what your code is doing for me. I was just talking about a gradual rotation. If it’s snapping back to an angle, it’s because your code is doing that in some way. Plus, I just dropped that code down as an example, not tested. It should be enough for you to figure it out along with the script reference for Slerp.
When I was looking at it, I did think it should do a check first to see if the rotation needs to be adjusted, and everything else be within that block. So something like
Quaternion lookRotation = Quaternion.LookRotation(playerVelocityAxis_Vector3)
if(transfrom.rotation != lookRotation)
{
// do all code concerning rotation
}
Like I say, it’s hard to read someone else’s code sometimes, so I don’t know if that would matter.
Thanks for the continued help.
line 31: transform.rotation = Quaternion.Euler(0f, transform.eulerAngles.y, 0f);
keeps the player character from tipping over.
Removing the line still does not fix the problem with and without your new check code.
So I added the code back in, put put it before the angle change.
Adding your check code produces this error:
“Look rotation viewing vector is zero
UnityEngine.Quaternion:LookRotation(Vector3)
PlayerInputScript:FixedUpdate() (at Assets/Scripts/PlayerInputScript.cs:60)”
Current Code (Still not working)
private void FixedUpdate()
{
playerXSpeed_float = Input.GetAxis("Horizontal");
playerZSpeed_float = Input.GetAxis("Vertical");
playerVelocityAxis_Vector3 = new Vector3(playerXSpeed_float, 0f, playerZSpeed_float);
playerVelocityAxis_Vector3 = Quaternion.AngleAxis(thirdPerson_Camera.transform.eulerAngles.y, Vector3.up) * playerVelocityAxis_Vector3;
targetRotation_Quaternion = Quaternion.LookRotation(playerVelocityAxis_Vector3);
transform.rotation = Quaternion.Euler(0f, transform.eulerAngles.y, 0f);
if (transform.rotation != targetRotation_Quaternion)
{
if (playerVelocityAxis_Vector3.magnitude > 0)
{
if (!rotating_bool)
{
startRotation_Quaternion = transform.rotation;
rotating_bool = true;
}
if (rotating_bool && transform.rotation != Quaternion.LookRotation(playerVelocityAxis_Vector3))
{
transform.rotation = Quaternion.Slerp(startRotation_Quaternion, Quaternion.LookRotation(playerVelocityAxis_Vector3), turnSpeed_float * Time.time);
}
else
{
rotating_bool = false;
}
}
}
GetComponent<Rigidbody>().AddForce(playerVelocityAxis_Vector3.normalized * playerAcceleration_float);
...
I really don’t understand why I’m having the instant rotation problem. The code and documentation seem like they would produce the desired results, but they don’t.
Just a note: You can freeze the rotation of the rigidbody, instead of that line of code that you’re using to keep it from “tipping over”. 
The viewing vector is zero if it’s looking at a Vector3(0,0,0);
You already have a check that the magnitude is above zero in the rotation block.
The lines don’t correspond so I don’t know what line the error is talking about.
Also, you have cached
targetRotation_Quaternion = Quaternion.LookRotation(playerVelocityAxis_Vector3)
so don’t reuse the method. use targetRotation every time after instead of that method.
Actually, I think you have to dynamically smooth the rotation like the second example here:
The reason it appeared to snap back is that it wasn’t dynamically changing with the rotation. It was using a start and finish. That means you can drop most of that code and just change your original.
Thanks for the tip, methos5k.
Line 60, corresponds to line 12:
targetRotation_Quaternion = Quaternion.LookRotation(playerVelocityAxis_Vector3);
I changed
to
and that fixed the error message.
Now I’m working on the suggestion for dynamic rotation…
Okay, I’m getting close
private void FixedUpdate()
{
playerXSpeed_float = Input.GetAxis("Horizontal");
playerZSpeed_float = Input.GetAxis("Vertical");
tiltAroundY_float = Input.GetAxis("Horizontal") * tiltAngle_float;
playerVelocityAxis_Vector3 = new Vector3(playerXSpeed_float, 0f, playerZSpeed_float);
playerVelocityAxis_Vector3 = Quaternion.AngleAxis(thirdPerson_Camera.transform.eulerAngles.y, Vector3.up) * playerVelocityAxis_Vector3;
if (playerVelocityAxis_Vector3.magnitude > 0)
{
targetRotation_Quaternion = Quaternion.Euler(0f, tiltAroundY_float, 0f);
if (transform.rotation != targetRotation_Quaternion)
{
if (!rotating_bool)
{
startRotation_Quaternion = transform.rotation;
rotating_bool = true;
}
if (rotating_bool && transform.rotation != Quaternion.LookRotation(playerVelocityAxis_Vector3))
{
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation_Quaternion, Time.deltaTime * smoothTurn_float);
}
else
{
rotating_bool = false;
}
}
}
GetComponent<Rigidbody>().AddForce(playerVelocityAxis_Vector3.normalized * playerAcceleration_float);
See line 11 rotates the directional input with respect to the camera’s perspective.
I need to do that with the code on line 15, but targetRotation_Quaternion is a Quaternion, not a Vector3, so I don’t know how to do that. Does anyone else know? Would that change also have to be made on the check in line 24?
You don’t need to do any of that. Go back to your first code post at the top of the page and change this line:
transform.rotation = Quaternion.LookRotation(playerVelocityAxis_Vector3);
to:
transform.rotation = Quaternion.Slerp(transform.rotation,Quaternion.LookRotation(playerVelocityAxis_Vector3),Time.deltaTime * smooth );
and add a float smooth of 2.0f
if the original code was working, that should slow down the turn.
That worked! Thanks so much for your help!
For future reference, there is a method called ‘RotateTowards’. 
Glad it worked out. I’ll check out RotateTowards, methos5k, thanks for the tip.