Hello everyone. Sorry for the bad english.
So how to fast and smooth move a 3D object?
Alright. This was probably asked a million times, and the answer in 99% was “use Vector3.Lerp”.
On a small speeds that work like a silk, indeed.
But,
what i’m speaking about is moving from the left corner of a full-screen game to the right corner in lets say 2 seconds (consider fast top-down space shooter). Also, i must say i’ve tried different “simple” approaches but they didn’t give the smoothness i talk about below. Everytime i had jitters.
So after two days of research i was able to implement (I hope so) a simplified RK4 integration for 3D, which was described by Glenn Fiedler here (the RK4 itself), here (RK4 for 3D) and here (fixing a timestep).
What i have now:
private struct Derivative
{
// Glenn's example include acceleration also, but i have constant speed.
public Vector3 velocity; // velocity
}
private struct PlayerPosState
{
public Vector3 position; // position
public Vector3 velocity; // velocity
}
Derivative Evaluate(
PlayerPosState initial,
float dt,
Derivative d)
{
PlayerPosState state;
state.position.x = initial.position.x + d.velocity.x * dt;
state.position.y = initial.position.y + d.velocity.y * dt;
state.position.z = initial.position.z + d.velocity.z * dt;
// Velocity doesn't change.
state.velocity = initial.velocity;
var output = new Derivative();
output.velocity = state.velocity;
return output;
}
private Vector3 Integrate(PlayerPosState state,
float dt)
{
Derivative a, b, c, d;
a = Evaluate(state, 0.0f, new Derivative());
b = Evaluate(state, dt * 0.5f, a);
c = Evaluate(state, dt * 0.5f, b);
d = Evaluate(state, dt, c);
var dxdt = 1.0f / 6.0f * (a.velocity + 2.0f * (b.velocity + c.velocity) + d.velocity);
state.position = state.position + dxdt * dt;
return state.position;
}
}
I’m pretty sure missing acceleration shouldn’t be an issue. Ok, and next here is where all time calculations happen, thats inside Update loop:
private TickTime = 0.01f;
private void UpdateToRenderSynchronized()
{
// Used UnityEngine.Time.deltaTime instead of counting
// currentFrameTime - lastFrameTime.
var frameTime = Time.deltaTime;
if (frameTime > 0.25f)
frameTime = 0.25f;
accumulator += frameTime;
while (accumulator >= TickTime)
{
lastPosState = currentPosState;
currentPosState = new PlayerPosState
{
position = localPlayers[0].transform.position,
// direction is simple built-in Vectors which depends on input.
velocity = localPlayers[0].direction * speed
};
localPlayers[0].transform.position = Integrate(currentPosState, (float)TickTime);
accumulator -= TickTime;
}
var alpha = accumulator / TickTime;
var endPosState = new PlayerPosState();
endPosState.position = (currentPosState.position * (float)alpha) +
(lastPosState.position * (1f - (float)alpha));
}
What i expected with all this
is jitterless and frame-rate independent behaviour. I don’t see where i made a mistake, nor where i made it different from what Glenn described (although he used C++).
Player object doesn’t have rigidbody, because no physics here.
Also i understand, that 2 seconds for lets say a 1024 pixels is 17 pixels “per step” considering that on monitor we only see 60 updates per second and the player would already seem not as smooth, but that’s not the problem. On the video below you can clearly see what i mean. Video is 60FPS. Also Application.targetFrameRate was set to 60, but as i’ve said it should be frame-rate independent anyways.
What i see
is jitters on frame-rate peaks and falls, but the overall FPS is high.
That shouldn’t depend “on maxFrameRate” (VSync) or targetFrameRate, but with different settings of that result is the same.
Hopefully i would get a promt from someone. Maybe i should make this physic based? But i don’t think so… From what i see i believe my mistake is somewhere in frame rates and stuff, not in the RK4.
There should be a way to make it frame-rate independent.
Thanks in advance.
Videos: