I have been working on a CharacterController for some time now (here is the current code):
using UnityEngine;
[RequireComponent(typeof(CharacterController))]
class MomentumMovement : MonoBehaviour
{
public GameObject playerCamera;
public GameObject playerModel;
CharacterController controller;
float speed = 400f;
Vector3 lastVelocity;
void Start()
{
controller = GetComponent<CharacterController>();
lastVelocity = controller.velocity;
}
Vector3 ScaleDirectionVector(Vector3 direction)
{
float multiplier = 1 / (Mathf.Abs(direction.x) + Mathf.Abs(direction.z));
return new Vector3(
direction.x * multiplier,
0,
direction.z * multiplier
);
}
void Move()
{
Vector3 moveVector = ScaleDirectionVector(playerCamera.transform.forward) * Input.GetAxis("Vertical");
moveVector += ScaleDirectionVector(playerCamera.transform.right) * Input.GetAxis("Horizontal");
moveVector *= speed * Time.deltaTime;
controller.SimpleMove(moveVector);
playerModel.transform.position = transform.position;
}
void RotateToVelocity()
{
Vector3 lookAt = transform.position + controller.velocity.normalized;
Vector3 targetPostition = new Vector3(lookAt.x, transform.position.y, lookAt.z);
if (targetPostition - transform.position != Vector3.zero)
{
Quaternion q = Quaternion.LookRotation(targetPostition - transform.position);
transform.rotation = Quaternion.RotateTowards(transform.rotation, q, 500 * Time.deltaTime);
}
}
Vector3 CalculateTilt(Vector3 acceleration)
{
return new Vector3(
acceleration.z,
0,
acceleration.x
);
}
void TiltToAcceleration()
{
Vector3 centerOfMass = controller.center + controller.transform.position;
Vector3 acceleration = controller.velocity / Time.deltaTime - lastVelocity;
Vector3 tilt = CalculateTilt(acceleration);
Quaternion targetRotation = Quaternion.Euler(transform.eulerAngles + tilt);
playerModel.transform.rotation = Quaternion.Lerp(playerModel.transform.rotation, targetRotation, 10 * Time.deltaTime);
}
void FixedUpdate()
{
Move();
RotateToVelocity();
TiltToAcceleration();
lastVelocity = controller.velocity / Time.deltaTime;
}
}
And I have been stuck for a few days with adding an "acceleration tilt" effect on the model of the player.
It is important to note that the script is attached to a GameObject with a CharacterController attached on it, while the playerModel is a separate object in the scene, I am trying to prevent some
local-global rotation problems this way (I might be wrong here)
For some reason (this is the best variation of the code) the acceleration tilt looks proper only while accelerating towards positive z.
Now, I know this has something to do with trigonometry but I have tried many variations of Sin(transform.eulerAngles.y) or Cos(transform.eulerAngles.y) * something in the calculations but still couldn't make it work properly.
Can someone please point out my mistake? Thank you all.
Note: There are weird jitters in the model movement too, if someone could help me with that too that would be great.
I was looking for a method like this one and I attempted to follow your code. I believe your Acceleration formula is wrong. Vector3 acceleration = controller.velocity / Time.deltaTime - lastVelocity; This is your acceleration formula, but it isn't correct because you are dividing by deltaTime - lastVelocity, on top of that your lastVelocity variable is being set to your velocity / Time.deltaTime, this is an issue and it is why your code is inaccurate and doesn't lean into turns or towards the actual velocity. your formula for acceleration should be this : Vector3 acceleration = (controller.velocity - lastVelocity) / timeTaken; You can't just divide by deltaTime because that isn't what the formula requires though most formulas use Δt as their variable for time it doesn't mean deltaTime in reference to unity. It represents the time taken to change from lastVelocity to the current velocity. This means you need to create a float variable called timeTaken that is constantly having Time.deltaTime added to it in update. You also need to make sure that lastVelocity is just set to controller.velocity and not divided by deltaTime. So in your code after setting lastVelocity start updating the timeTaken and when you call TiltToAcceleration after tilting reset timeTaken to zero and update lastVelocity. You could also use a stride wheel to track distance and update the variables that way because that is what I am doing for my game, I'm not going to explain that as it would be much more complicated but it would also result in more accurate Tilt. I am aware that I am late to the topic but this is a way to fix the jittering and making it lean into turns. Also don't use fixedUpdate for everything, it is good to use it for Rigidbody physics updates (as long as you have your body set to interpolation) but it is updated at a very different timestep to the normal update method. So ensure that you use update for rotations, and you will see how smooth it is.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With