I've just recently been introduced to Time.deltaTime and lerping and have been kinda stuck for the past few days.
So when I plug in Time.deltaTime for the last float and then multiply by another float variable called speed, what exactly is happening? What is Time.deltaTime doing to speed each frame that gives the end result?
Thank you!!
public class CameraMovement : MonoBehaviour
{
public GameObject followTarget;
public float moveSpeed;
void Update()
{
if (followTarget != null)
{
transform.position = Vector3.Lerp(transform.position, followTarget.transform.position, moveSpeed * Time.deltaTime);
}
}
}
Vector3.Lerp expects a factor between 0 and 1 and interpolates the two positions so
0 would mean transform.position
1 would mean followTarget.transform.position
any other value between those the interpolation between them meaning something like
transform.position + (followTarget.transform.position - transform.position) * factor;
Time.deltaTime = time past since last frame rendered so most likely a value close to 0.017 (for 60FPS). You can calculate by yourself what value this gives you in average for moveSpeed * 0.017.
It looks like what you are actually looking for is rather Vector3.MoveTowards
void Update()
{
if (followTarget != null)
{
transform.position = Vector3.MoveTowards(transform.position, followTarget.transform.position, moveSpeed * Time.deltaTime);
}
}
Using Time.deltaTime * scalar in Lerp is perfectly fine, but it's not the "expected" way to use it.
LERP means "Linear intERPolation". With this mathematic tool, you are able to interpolate a value between two others using a linear function ( y = ax + b ) and a coefficient t between 0 and 1.
Supposing you are trying to get a position (C) between two others (A and B) (like your problem), you have the folowing drawing :
1°) If the initial and destination position does not change, having the parameter t vary between 0 and 1, the movement curve will be perfectly straight : the speed will be the same over time.
transform.position = Vector2.Lerp(InitialPosition, DestinationPosition, t ); // t from 0 to 1
2°) However, having t (almost) not vary over time (with Time.deltaTime), but chaning the position of A, the movement will decelerate over time, because you take a fixed percentage of the remaining distance each time. Since the distance decrease over time, you travel less distance over time too
transform.position = Vector2.Lerp(transform.position, DestinationPosition, 0.017 ); // 0.017, approx of Time.deltaTime at 60FPS
Related
I am trying to create a boundary for my level in Unity 3d, and I do not want my character to be able to move past a certain value on the x-axis. It works for the right side, I can move right and my character doesn't go past LevelBounds.rightSide. However, for the left side, once I hit LevelBounds.leftSide, my character gets stuck and I can no longer move anymore. I am also noticing that the x position actually exceeds the LevelBounds.leftSide. Here is my update() function:
void Update()
{
horizontalInput = Input.GetAxis("Horizontal");
if (this.gameObject.transform.position.x > LevelBounds.leftSide)
{
transform.Translate(Vector3.right * Time.deltaTime * turnSpeed * horizontalInput);
}
if (this.gameObject.transform.position.x > LevelBounds.rightSide)
{
transform.Translate(Vector3.left * Time.deltaTime * turnSpeed * horizontalInput);
}
player.transform.Translate(Vector3.forward * Time.deltaTime * speed);
}
You using an input axis that ranges from -1 to 1. You should either use:
Math.Abs(horizontalInput);
or:
Vector3.right
on both time.
I'm trying to make an airplane controller, I am kind of aiming for something between arcade and realistic, so I want the plane to turn with a force proportional to the roll.
I haven't coded in any adjustments and I'm still prototyping the whole thing, but I encountered a problem with getting the signed rotation angle while using quaternions, I had a look at Determining if quarternion rotation is clockwise or counter clockwise here on SO but I am having trouble generalizing the solution to the (almost) arbitrary plane the rotation can be at.
What I made by now:
private void FixedUpdate()
{
float desiredYaw = _yaw * _rotationSpeed * Time.fixedDeltaTime;
float desiredPitch = -_pitch * _rotationSpeed * Time.fixedDeltaTime;
float rotationStepSize = _throttle * Time.fixedDeltaTime;
Quaternion toRotate = Quaternion.Euler(desiredPitch, 0, desiredYaw);
Quaternion straighRotation = Quaternion.LookRotation(_transform.forward, Vector3.up );
_rotation = _transform.rotation * toRotate;
float turningForce = Quaternion.Angle( _rotation, straighRotation );
_rigidbody.MoveRotation( _rotation );
_rigidbody.AddTorque( turningForce * _rotationForce * rotationStepSize * Vector3.up );
_rigidbody.AddRelativeForce( _speed * rotationStepSize * Vector3.forward );
}
EDIT: I realized I'm calculating the turning force using the roll rather then the yaw, that was intended just wrong wording, corrected now.
Since all you need is a factor that describes how downward the plane's right is, you can just use the y component of the plane's right for that. No need to bring in quaternions or even trigonometry. Explanation in comments:
private void FixedUpdate()
{
// ...
// Calculate how downward local right is in range [-1,1]
// The more downward, the more tilted right the plane is
// positive = tilted right
// negative = tilted left
float turnFactor = -_transform.right.y;
// Could do things to modify turnFactor to affect easing here.
// For instance, if turning rate should start slower then rapidly increase:
// turnFactor = Mathf.Sign(turnFactor) * turnFactor * turnFactor;
// Use factor and _rotationForce member to calculate torque, apply along
// global up.
// We expect to call this every fixed frame so we can just use the default
// ForceMode of ForceMode.Force which multiplies fixed delta time inside.
_rigidbody.AddTorque(_rotationForce * turnFactor * Vector3.up);
// ...
}
private void RotateTargetsRandom()
{
timer += Time.deltaTime;
if (timer > rotationTime)
{ // timer resets at 2, allowing .5 s to do the rotating
qTo = Quaternion.Euler(new Vector3(0.0f, Random.Range(-180.0f, 180.0f), 0.0f));
timer = 0.0f;
}
foreach (var target in targets)
{
target.transform.rotation = Quaternion.Slerp(target.transform.rotation, qTo, Time.deltaTime * speed);
}
}
The function make that each 2 seconds the targets(GameObjects) will rotate.
But in the comment not my comment it say "timer resets at 2, allowing .5 s to do the rotating"
Where is the 0.5 part in the code ?
So each 2 seconds there is a rotation and the rotation is taking 0.5 seconds. I just don't understand where is the 0.5 part ? And if I want it to rotate duration of 0.1 time instead 0.5 ?
What makes it rotate following a speed is the
target.transform.rotation = Quaternion.Slerp(target.transform.rotation, qTo, Time.deltaTime * speed);
Slerp is the spherical interpolation and it is basically interpolating between the current rotation of the object and the qTo value using the deltaTime (elapsed time since last frame in seconds) and an arbitrary speed.
e.g.
imagine you are only rotating in one axis to simplification, say the z-axis.
if the rotation is 0 and qTo is 45 in the first iteration it will be:
45 * deltaTime * speed
But since it is already set it to the transform rotation the next iteration will use the new rotation as the initial value of the interpolation:
(45 - previousRotation) * deltaTime * speed + previousRotation
Thus it is converging to 45 when speed * sum(all deltaTimes) is one.
So I suppose your speed is 2 since it is taking 0.5s to reach the target rotation.
But the code is a little flaky since it would behave weirdly if rotationTime is lower than 1 / speed.
It is not specified in the question but I believe you are calling it inside an Update(ish) method, to allow deltaTime to have a reasonable meaning.
So to control the duration of your rotation you set the speed to a desired value, to have a 0.1s duration set the speed to 10.
How can I keep the diagonal speed to be the same as the horizontal and vertical speed without clamping any value or using ".normaized". I tryed to normalize the values but I lost the joystick values between 1 and 0. Here is my code :
void ListenInput()
{
Vector3 rightDirection = camera.right;
Vector3 frontDirection = camera.GetForwardFromAngleY();
move = new Vector2(
Input.GetAxis("Horizontal"),
Input.GetAxis("Vertical")
);
MoveCharacter(rightDirection * move.x);
MoveCharacter(frontDirection * move.y);
}
void MoveCharacter(Vector3 velocity)
{
transform.position += velocity * Time.deltaTime * runningSpeed;
}
Here, you should clamp the magnitude of the input Vector2.
For example with Vector2.ClampMagnitude() from the Unity API.
That will keep the input non-binary and prevent the diagonal from getting larger than purely horizontal/vertical inputs.
void ListenInput()
{
Vector3 rightDirection = camera.right;
Vector3 frontDirection = camera.GetForwardFromAngleY();
move = new Vector2(
Input.GetAxis("Horizontal"),
Input.GetAxis("Vertical")
);
move = Vector2.ClampMagnitude(move, 1f);
MoveCharacter(rightDirection * move.x);
MoveCharacter(frontDirection * move.y);
}
void MoveCharacter(Vector3 velocity)
{
transform.position += velocity * Time.deltaTime * runningSpeed;
}
If you normalize a vector you will make sure it's length is 1. This is a great way to avoid quirks like "diagonal movement is faster than normal movement".
However, the fact that the length is always 1 also means that there is no "move slowly" or "move at full speed" distinction from the joystick. When you say "I lost the joystick values between 1 and 0" is due to this fact.
One way developers get around this is by using a mathematical formula to scale the speed.
You could:
Use the largest value (horizontal or vertical) to control the speed
Use the smallest value
Use a combination of the two
Another way to do this is to store how long ago the movement started, then scale the speed based on that. This method has its own challenges, but is very familiar to players.
Examples
For instance, if I have:
horizontalInput = 1
verticalInput = 0.5
This means my normalized vector looks like this:
I could:
Use the largest value
Move at full speed (1) on the direction of my vector.
Use the smallest value
Move at half speed (0.5) on the direction of my vector.
Use a Use a combination of the two values
For this instance, lets use the following formula: (x+y)/2.
Move at 3/4 speed (0.75) on the direction of my vector.
NOTE: This formula will not "feel" as nice if you have x=0 and y=1, this is just an example. You most likely want to use Min, Max, Avg and if-clauses to control how the speed works.
You can use different formulas and different techniques to make the movement in your game feel like what you want, but take the time to analyze WHY it feels like that.
im trying to move a object in unity between 2 points, and at the moment it kinda works strange, i read the documentation and it says that the object begin point is (0,0,0) so my object goes under my other mesh that i have there, and the end point i can actually control, in my case it is 10, i want the object to move between 1.5 and 10(not 0 to 10)
i have this
void Update () {
transform.position = new Vector3(transform.position.x,Mathf.PingPong(Time.time,10.0f), transform.position.z);
}
when i try to put speed on the ball doing this:
void Update () {
transform.position = new Vector3(transform.position.x,Mathf.PingPong(Time.time,10.0f) * 10, transform.position.z);
}
the object does not colide and goes back at the end point it just stop looping and never came back how can i correct this 2 problems?
If your object has a collider, I suggest you move it via its Rigidbody rather than its Transform, to avoid potential collision issues. Try this:
public float MinY = 1.5f; // y position of start point
public float MaxY = 10f; // y position of end point
public float PingPongTime = 1f; // how much time to wait before reverse
public Rigidbody rb; // reference to the rigidbody
void Update()
{
//get a value between 0 and 1
float normalizedTime = Mathf.PingPong(Time.time, PingPongTime) / PingPongTime;
//then multiply it by the delta between start and end point, and add start point to the result
float yPosition = normalizedTime * (MaxY - MinY) + MinY;
//finally update position using rigidbody
rb.MovePosition(new Vector3(rb.position.x, yPosition, rb.position.z));
}
Here you have a better control on the distance to travel, and the speed.
Actually I didn't get exactly what are the problem you faced. But don't forget here and in your try, that you are directly modifying the position of the object, not adding forces or else.
Hope that helps you.
I think you simply misunderstood how the Mathf.PingPong method works :
first argument t is the value you want to "clamp" between 0 and the given length : this is were you want to put the Time.time as you did since this value will increase over time and therefore perpetually oscillate. If you want to increase/decrease the oscillation speed you have to multiply it.
second argument length is the max value of the "clamp" : if you want to increase/decrease the distance (in your case) you have either set it to 0 and multiply the whole Mathf.PingPong(...) by a value or directly give it the wanted value (both implementations will have a different effect.
Mathf.PingPong(Time.time * speed, 1.0f) * value : speed will affect the oscillation speed / value will affect the max value reached AND the speed / time to complete the oscillation (back and forth) will remain the same as value changes and decrease as speed increases
Mathf.PingPong(Time.time * speed, value) : speed will affect the oscillation speed / value will affect the max value reached BUT NOT the speed / time to complete the oscillation (back and forth) will increase as value increases and decrease as speed increases
About your other problems :
If you want to move your object between 1.5 and 10 you have to write something like this :
transform.position = new Vector3(transform.position.x, 1.5f + Mathf.PingPong(Time.time, 10.0f - 1.5f), transform.position.z);.
Also if you want to detect collision, avoid setting position manually as it will mess up with Physics and cause weird behaviors. Best way to move your object while keeping physic working is to do as #Heldap said using Rigidbody.MovePosition.