Thursday, March 21, 2019

Unity 3D - Rotating an object in relation to its velocity


I am currently trying out Unity 3D and I've run into a problem. The problem is I have a car that moves along a road and then when it reaches a corner I would like it to turn the corner and have the model rotate based on its current velocity.


Here is the code I have for moving the object forward:


_velocity += (_velocity * Time.deltaTime) + (_acceleration / 2f) * Time.deltaTime * 2f;
_velocity = Mathf.Clamp(_velocity, 0f, MaxVelocity);

Vector3 position = this.transform.position;
position += -this.transform.forward * _velocity * Time.deltaTime;
this.transform.position = position;

The variables are declared as class members:


private const float MaxVelocity = 10;
private float _velocity;
private float _acceleration;

This appears to work OK for moving forward. If I applied a rotation it may look OK for a certain velocity, but if the velocity changes, then the rotation seems to quick or to slow.



What I would like (but completely failing at accomplish) is when it comes time to turn, then I would look at the velocity and calculate a smooth rotation so when an object performs a turn it will always end up at the same point regardless of how quick it approaches the corner.


I have tried a number of options, over countless hours, but I'm not having any luck!


Any ideas?


UPDATE #1:


Below is a half-way solution I got, but for now I will add it here and leave the question open for a bit in case someone can provide a better approach.


From what I worked out a LERP rotation from the current direction to the new direction did the trick (kind of!). So, I created a behaviour called VehicleController and in the Awake method I added this:


_target = this.transform.rotation * Quaternion.Euler(0f, 90f, 0f);

This gave me the target rotation relative to my current rotation, i.e. my rotation would have to equal _target for me to be facing in the desired direction. I then added the following to the FixedUpdate method:


//  Rotate

if(_doTurn)
{
this.transform.rotation = Quaternion.Lerp(this.transform.rotation, _target, Time.fixedDeltaTime * _velocity);
}

This performed the actual rotation. The key part was setting the third parameter Time.fixedDeltaTime * _velocity. This meant that my rotation speed matched my velocity.


My final FixedUpdate method looked like this:


//  Forward
_velocity += (_velocity * Time.deltaTime) + (_acceleration / 2f) * Time.fixedDeltaTime * 2f;
_velocity = Mathf.Clamp(_velocity, 0f, MaxVelocity);

Vector3 position = this.transform.position;
position += -this.transform.forward * _velocity * Time.fixedDeltaTime;
this.transform.position = position;

// Rotate
if(_doTurn)
{
this.transform.rotation = Quaternion.Lerp(this.transform.rotation, _target, Time.fixedDeltaTime * _velocity);
}


In this sample I had a BoxCollider that triggered the turn, so in the OnTriggerEnter method I simply set _doTurn to true.


Now there are a few things with this:



  1. After the turn I had to fix the positioning and rotation a little. The values were off by about .2, but I couldn't really notice any oddness to care.

  2. This approach gives a quick snap change in direction which wasn't what I was really after. Ideally I wanted a situation where the turn would be gradual at a certain angle and end in the same point. But, to be honest, it looks OK and that gets me moving on for now! :)


With regards to #1 I think I had to fix some of the position/rotation values because I was using a BoxCollider to trigger the turn so there may be better approaches.




No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...