Easing to target with delta time

Started by
2 comments, last by sundersoft 11 years, 11 months ago
Ok, so I'm having some problem figuring out how to ease (slide to a point) an object with a delta time.

in my simulation "dt" represents delta time as a difference in frame rate. So with a target frame rate of 60 and a simulation rate of 60, the dt will be 1. Simulation running at 30 will return a dt of 2.

the integration for an object is:


_invMass = 1.0; //assume mass is always 1 for now

_acceleration.x = _force.x * _invMass;
_acceleration.y = _force.y * _invMass;

_velocity.x += _acceleration.x * dt;
_velocity.y += _acceleration.y * dt;

limitVelocity(maxSpeed);

x += _velocity.x * dt;
y += _velocity.y * dt;

//clear force

_force.x = _force.y = 0;


before integrating the object, it's easing function is called:



//find distance from target (tx, ty) to position (x, y)

var dx:Number = tx - x;
var dy:Number = ty - y;
var len:Number = Math.sqrt(dx * dx + dy * dy);

if (len > 0) {

// make sure the object eases to the position
// I think the problem is here, the force needs to make sure the object won't overshoot the target, but I'm not sure how to use delta time in this equation

var speed:Number = Math.min(len, maxSpeed);
var force:Number = speed / len;

//find desired velocity, could also multiply by another scalar for smoother easing

var dvx:Number = dx * force;
var dvy:Number = dy * force;

//add the force as the difference from desired to current velocity

_force.x += dvx - _velocity.x;
_force.y += dvy - _velocity.y;
}


as you can see I think the issue and solution lies in calculating the force, but I'm not sure how to plug "dt" in there.
The issue that happens is that the object will overshoot the target and continually jitter back and forth never reaching the position.

any ideas?

Thanks.
Advertisement
I recently wrote an algorithm that does this, although it works by applying either a fixed force or its negation. Also, your position calculation is wrong for the integration. You need to use [font=monospace]"[/font]x += (0.5*_acceleration.x*dt+_velocity.x) * dt;" before assigning the new velocity if you want it to be accurate at large timesteps.

The algorithm works by applying the fixed force in some direction for a certain amount of time, then reversing the force for another period of time and then applying no force after the destination position and velocity are achieved. It will always try to minimize the total time. It should probably be run every frame to compensate for disturbances to the object. It shouldn't overshoot unless the object is travelling too fast to arrive at the destination without overshoot.

To use this algorithm, you need to specify:
-displacement: The position of the object with the destination point as the origin (i.e. object position - destination position). If the destination is moving, then you should use its position as it would be at the start of the next frame.
-target_velocity: The velocity of the destination point. It should be the value as it would be at the start of the next frame if the destination is moving. For a stationary destination, use 0.
-velocity: The current velocity of the object in the current frame.
-acceleration: The maximum acceleration to apply.

It outputs two velocity changes. The first change (x component of the vector) should be done in the current frame before physics calculation, and the second should be stored and applied in the next frame. This simulates forces that last for only part of the frame.

The algorithm should be used once for each axis.

To use a maximum force instead of a maximum acceleration, divide the maximum force by the object's mass to get the acceleration and multiply the outputs by the mass to get the impulses that need to be applied to the object.

The algorithm is written in C# but should be easy to port to other languages.

It also needs this function, which returns the roots of a quadratic function in a numerically stable way:

//returns least root as x, greatest one as y; returns null if no roots
public class quadratic_roots {
public static Vector2? _(float a, float b, float c) {
double d_a=a;
double d_b=b;
double d_c=c;
var d=(float)(d_b*d_b-4*d_a*d_c);
if (d<0) return null;
d=Mathf.Sqrt(d);
float q;
if (b<0) {
q=-0.5f*(b-d);
} else {
q=-0.5f*(b+d);
}
var x1=q/a;
var x2=c/q;
if (x1>x2) {
return new Vector2(x2, x1);
} else {
return new Vector2(x1, x2);
}
}};



Vector2 axial_force(float displacement, float target_velocity, float velocity, float acceleration) {
//tout="";
float dt=Time.deltaTime;
var p0=displacement;
var v0=velocity;
var vf=target_velocity;
var A=acceleration;
//
var v0_minus_vf=v0-vf;
Vector2? negative_tas=quadratic_roots._(-A, 2*v0_minus_vf, -0.5f*v0_minus_vf*v0_minus_vf/A+p0+dt*vf);
Vector2? positive_tas=quadratic_roots._(A, 2*v0_minus_vf, 0.5f*v0_minus_vf*v0_minus_vf/A+p0+dt*vf);
//bool is_negative=false;
float ta=Mathf.Infinity;
float w=Mathf.Infinity;
float s=0;
for (var x=0;x<4;++x) {
bool t_is_negative=x>=2;
Vector2? tas=t_is_negative? negative_tas : positive_tas;
if (tas==null) continue;
float t_s=t_is_negative? -1 : 1;
float t_ta=tas.Value[x&1];
float t_w=2*t_ta+t_s*v0_minus_vf/A;
//print(x +" X , "+ t_s +", "+ t_ta +", "+ t_w);
if (Mathf.Abs(t_ta-t_w)<0.00001f) t_ta=t_w;
if (t_w<t_ta) continue;
if (t_w<w) {
ta=t_ta;
w=t_w;
s=t_s;
}
}
if (s==0) print("should never happen. ta="+ ta +", w="+ w +", p0="+ displacement +", v0="+ velocity +", vf="+ vf +", A="+ A);
//
if (w<0) w=0;
if (ta<0) ta=0;
if (ta>dt) ta=dt;
if (w>dt) w=dt;
var mysterious_number=(0.5f*w*w - ta*ta)/dt;
var sA=s*A;
var va=sA*(mysterious_number - w + 2*ta);
var vb=sA*(-mysterious_number);
return new Vector2(va, vb);
}


Tell me if you need help porting/understanding this algorithm or if you want the math behind it if you decide to use it.
thanks for the reply but that seems a bit too intensive for my purposes, this is going to be applied to many objects.

I tried implementing what you said with the integration but it seemed to make things worse, the jittering got much larger. Is this the correct order I should be running:

1. advance position using formula ( [color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

[background=rgb(250, 251, 252)]pos += (0.5 * acceleration * dt velocity) * dt )[/background]

[/font]

2. add force to acceleration (acceleration = force / mass)


3. add acceleration to velocity (velocity += acceleration * dt)


thanks for the reply but that seems a bit too intensive for my purposes, this is going to be applied to many objects.

I tried implementing what you said with the integration but it seemed to make things worse, the jittering got much larger. Is this the correct order I should be running:

1. advance position using formula (

[color=#282828][font=helvetica,arial,verdana,tahoma,sans-serif]

[background=rgb(250, 251, 252)]pos += (0.5 * acceleration * dt + velocity) * dt )[/background][/font]



2. add force to acceleration (acceleration = force / mass)


3. add acceleration to velocity (velocity += acceleration * dt)




The changes to the integration won't help the jitter since the equation you're using needs to be changed, but the position is going to be dependant on the time step length (i.e. a simulation at 30fps would have different results from a 60fps simulation, with the 60fps results being more correct) if you don't calculate the position correctly. This might not be relevent to you.

Also, you need to compute the acceleration first, then the position, and then the final velocity. So, steps 2 and 3 should be interchanged.

You can try implementing a dampened spring, which will cause the object to overshoot but converge quickly.
http://en.wikipedia.org/wiki/Damping#Example:_mass.E2.80.93spring.E2.80.93damper

This topic is closed to new replies.

Advertisement