Kinematic movement interpolation and fixed_update()

I faced with some problems in implementation of manual kinematic movement with using fixed_update() and fixed_update_frequency.

To move the kinematic body I set go position in fixed_update(), then get the contact_point_response messages and correct the position according the collisions. Then in the update() function I interpolate the position between the previous one and the new one according the time.

function fixed_update(self, dt)
  self.last_fixed_update_time = os.clock()
  self.last_fixed_update_dt = dt

  self.initial_position = self.position
  
  -- Next code changes self.position according the movement.
  -- ...
end

function update(self, dt)
  local current_update_time = os.clock()

  local interpolation = (current_update_time - self.last_fixed_update_time) / self.last_fixed_update_dt
  interpolation = math.max(0, math.min(1, interpolation))

  local lerp_position = vmath.lerp(interpolation, self.initial_position, self.position);
  go.set_position(lerp_position)
end

where:

  • self.initial_position — an old position before moving, taken from beginning of fixed_update().
  • self.position — a new calculated position after running fixed_update() and handling thecontact_point_response messages.

The result with fixed_update_frequency = 10 to hyperbolize visual problems:

In the video you can see that every 1/10th of a second fixed_update() is called and the future position is set to check for collisions. And immediately a rendering takes place. Then in the next frames an update() is executed, where the correct interpolated position is set.

So, the question

  • Is it okay that after a position change in fixed_update() the rendering happens? Or this is a bug and rendering should only happen after update()?
  • If this is okay, is the solution to move the kinematic collision object separately from the capsule model? I.e., move the model+camera only in the update() loop and move the collision object only in fixed_update() to avoid this invalid frame? Sounds logical, but it breaks the integrity of the object.
  • Any other ideas how to solve it?

More context:

Just in case, the order of the update/fixed update/render calls in Defold is:

  • :arrow_right: new frame
    • each proxy:
      • update()
      • fixed_update()
    • bootstrap collection:
      • update()
      • fixed_update()
    • render
  • :arrow_left: repeat

Plus, maybe this issue is related to your question - https://github.com/defold/defold/issues/7277

3 Likes

I think that external time counters like os.clock() (and socket.gettime()) would not help in this task. In a perfect test environment (when, for example, fixed update = 10, update = 60) everything will work absolutely brilliant, but when FPS starts to float, then nuances will come out and the interpolation won’t work. And it will not work with non-standard proxy settings, right?

So, ideally, Defold should have and give access to internal time counters (running separately for update and separately for fixed update). On these counters you can achieve a perfect interpolation :ok_hand:
And you mentioned Unity - so in that engine developers usually use the variables Time.time and Time.fixedTime for that.

(from my view!)

2 Likes

Thanks, useful note! Didn’t find it before.

It looks very similar. I’m also just looking for a way to catch the moment after the physics update, this will allow me to bring the position of the capsule back before rendering I think (but not sure about details)

May be you right, I did’n think abot it yet. Still testing in bootstrap collection.

Yes, we don’t have it so I use manual counting of os.clock().

I think this is similar to what I have seen in Britzl ‘Rotate and move’ public example.
My knowledge is limited so I may be some what incorrect…
I think that the issue is having more than one thing setting/controlling a game object position. The collision-physics-code and the lua-script fight each other.
If the collision-physics-code returned a result for the lua-script to handle
the problem would be fixed. Maybe there is a way to do this?

On collision detection the collision-physics-code sets the player position (to:next to the wall) If the detection distance is greater than the distance the player moves in a time step then there is no escape of the wall, if differences are small then it might delay movement.

In Britzl ‘Rotate and move’ public example, the player can end up sticking to the wall.
Sticky wall: Move the player left into the wall, slide slightly down the wall, move the player right, away from the wall, the player does not move. This does not always occur, so a few repeats maybe required.

Adding debug print statements to the update script of the x,dx positions shows the script does move the player away for a time step, but then collision detection grabs it back on the next time step. I guess this is a well known issues all game engines face.
Outward facing normals and direction vectors maybe.