Actually that’s the correct way. By multiplying with Time.deltaTime you’re taking into account the frame length.
You’re saying “during this fraction of time, how much does the acceleration affect the speed?”
Here’s my generic “move ballistic stuff in 2D” script… note how it uses Time.deltaTime several times: damping, accelerating and the actual movement.
You can delete the collision delegates (or leave them null) if you don’t want to use them.
using UnityEngine;
using System.Collections;
// @kurtdekker - cheesy 2D ballistics with optional collisions
public class Ballistic2D : MonoBehaviour
{
public Vector2 velocity;
public float damping = 0.2f;
public System.Func<Vector2, bool> checkCollisionDownward;
public System.Func<Vector2, bool> checkCollisionLateral;
void Update ()
{
// damp the velocity
velocity -= velocity * damping * Time.deltaTime;
// accelerate due to gravity
velocity += Physics2D.gravity * Time.deltaTime;
// compute our new position
Vector2 position0 = new Vector2( transform.position.x, transform.position.y);
Vector2 position = position0;
position += velocity * Time.deltaTime;
// possible collision check(s)
if (checkCollisionDownward != null)
{
if (checkCollisionDownward( position))
{
// <WIP> put code to back ourselves up to precisely where we hit
// <WIP> put any code here to cause bouncing/material-ish behaviors
position.y = position0.y;
velocity.y /= 2;
}
}
// possible collision check(s)
if (checkCollisionLateral != null)
{
if (checkCollisionLateral( position))
{
position.x = position0.x;
velocity.x /= 2;
}
}
// write new position
transform.position = position;
}
public void AddVelocity( Vector2 impulse)
{
velocity += impulse;
}
}
Like Kurt shows it should be:
velocity+=acceldeltaTime
position+=velocitydeltaTime
But deltaTime won’t make your game truly frame rate independent.
In a casual game the player probably won’t notice any difference in movement speed when playing the game with vsync on or off. But in a competitive game there will be some subtle differences that could lead to players with considerably different frame rates having an advantage or disadvantage, like being able to jump higher or further.
This is ultimately why I gave up on the character controller.
In a competitive game I’d recommend using a rigidbody and FixedUpdate for anything player related.
If you’re not using physics because you don’t want to move objects around with AddForce etc then perhaps you could use a kinematic rigidbody with MovePosition and MoveRotation. You’ll then get the benefit of a fixed frame rate but with smooth/interpolated movement.
Other than that the only alternative is to reinvent the wheel and create your own FixedUpdate and interpolation. But if you do this then don’t tell anybody or you could end up being admitted to a mental hospital.
You’re correct that an accelerating object will move faster with a faster time step. When moving at a constant speed, x+=speedPerSec*Time.deltaTime gives perfect results for any time step, but with acceleration it’s like the Fundamental Theorem of Calculus. Adding an amount to the speed every 0.1 seconds is like estimating the value of a curve using cubes of width 0.1 under that curve. There’s an undercount which accumulates. If you’re curious how large it is, try the calc trick where you compute with undercubes and again with overcubes and subtract (it’s easy – just add the speed either before or after). Maybe that’s a small enough number or maybe you can add a fudge factor to get it small enough.
If you want it to be 100% accurate, just use the closed formula: d=vt+at^2/2+c. Each frame plug in the time in seconds to get the position. If/when the object changes speeds you’d need to compute a new base position, base speed, and reset the base time. It doesn’t seem too bad (I’d make a class that knows the time, with getPos() and resetAccell(newAc)) but I’d do it side-by-side with maybe a rigidbody just to make sure it’s in the ballpark.
This may be accurate, but it will lose precision. With an IEEE754 32-bit float for time, you will lose a bit of precision whenever the Time.time value doubles. So in the first 16 seconds you’ve lost four bits of time precision. In half an hour, you’ve lost 11 bits of precision.
As Isaac Newton found, the fundamental nature of motion can be calculated either with gross measurements like “the sun is overhead again,” or the sum of infinitely fine deltas along a continual curve. We can’t calculate with an infinitesimal ∂_t_ so we are not going to precisely calculate infinitesimal ∂_x_ changes in position, either. But by continually using very small values for ∂__t__ over the course of each frame, we get the best we can do. It keeps your time values small so they don’t lose precision.
And unless you’re only talking about a very limited system with TWO BODIES interacting with each other, like one ball and one planet’s gravity, this is the only way to predict motions; this is why they call it the THREE BODY PROBLEM. This technique is called ‘forward integration’ and is used by NASA on every rocket mission, and anyone else trying to predict where things will end up in the future (albeit with slightly deeper floats or other number encoding). It’s imperfect, and varying your deltaTime every frame does make it slightly worse, but every machine is imperfect.
For simple uniform accelerated behaviour, there’s an easy trick to get around the framerate problem. The solution is to apply the movement in two steps. Apply half the velocity before the acceleration and the other half after the acceleration addition. This will work with any framerate. However, it should be clear that any kind of events or changes like a ramp up in acceleration would break this assumption. For most things this should be good enough.
pos += vel * dt * 0.5f;
vel += acc * dt;
pos += vel * dt * 0.5f;
There is not any more “proper” way as we’re dealing with time discrete simulations here. So this would never represent reality which does an actual integration (in a sense infinite frame rate).
The only alternative is to not doing “relative” animations using deltaTime but using absolute time as reference. However this doesn’t work well with any change in acceleration at all, so pretty useless in most usecases.
This but in FixedUpdate or an equivalent fixed-framerate simulation.
Of course you will need to also do the movement in FixedUpdate which means you will need to create a system of interpolation if you care about having buttery smooth-looking movement.
By the time you do that you may as well just use the physics engine anyway.
You actually got this the other way round. People with faster computers would be accelerating slightly slower. When your framerate drops you would be moving a bit faster. Over here I posted a table with 3 different position values at a frame rate of 10 fps. pos1 is the usual approach, pos2 is the correct position based on absolute time how it would work in the real world and pos3 is the position based on the half-half-trick. As you can see, at a lower framerate you would move further than the one at a better PC. The closer you get to an infinite framerate, the closer you get to the “correct” values.
Just think about an extreme case of just one frame per second. At a constant acceleration the position would be 0.5*acceleration*t*t.
Since t is 1 we simply get half the value of the acceleration which would be correct. Though the normal approach means, velocity after 1 second should have the “full value” of acceleration (since acceleration is speed per second). This is actually correct. However when we now just add the velocity with dt to the position we would move twice as far as we should have in that timespan. So the smaller the timespan, the more accurate it gets.
The half-half approach does move not enough in the first step but too much in the second and they perfectly fix each other.
FixedUpdate doesn’t really make the calculations “correct”, it just makes it “consistent”. Most physics systems for games simply use a fix timestep because the half-half-trick only world with constant accelerations. So acceleration itself must not be ramped up / down itself. You also run into other issues when it comes to drag and collision forces which is extremely hard to get “right”. That’s why we simply stick to a fixed update and call it a day. Unity’s / PhysX’s drag also has nothing to do with real world physics as it’s just a linear percentage . Though since we don’t really have actual wind, air density and so one to calculate the area of attack and CV value for your object moving, we simply get some mechanism which slows the object down over time.
This particular solution is covered exactly in the chapter 8:25-10:30 of the video above (time stamp). So if anyone needs a visual tutorial to better grasp the delta times and what they do exactly, this is why I think this Jonas’s video is magnificent.
Yes, I’ve seen a couple of videos in recent years which cover this quite well. Note that you “could” do the speed and position update each in one line, but it would require an additional quadratic term. You could do
pos += vel * dt + 0.5f * acc * dt * dt;
vel += acc * dt;
I guess it would also work when you switch the two lines but you would have to subtract the quadratic term. Effectively we’re working with absolute time in small time intervals. So “vel*dt” is just the linear starting speed and the quadratic term is just the usual uniform accelerated position after “dt” time. Though it’s obvious that the half-half approach is actually simpler. Of course you could factor out one “dt” and get
pos += (vel + 0.5f * acc * dt) * dt;
vel += acc * dt;
But that doesn’t really make it faster or more readable. Also it’s much easier to remember the half-half trick