From jittery to smooth: A practical guide for physics games

Defold 1.12.0 introduced one of the most impactful improvements for physics-heavy games: the reworked script callback order, that clearly separates now a fixed-timestep simulation from frame-timestep gameplay logic, and adds a dedicated “late pass” stage for e.g. camera or visual alignment.

As of Defold 1.12.0, the engine update loop now runs Lua script callbacks in this high-level order:

  1. fixed_update
  2. update
  3. late_update

Between these stages (and between all internal component-type updates), Defold dispatches and flushes posted messages, and all transforms are updated whenever needed.

Let’s go through what each type of update is useful for and how to utilize them in the best way, especially if you are working on games with physics simulations.

Fixed Update

Fixed Update, introduced earlier and used mostly in physics based games, since 1.12.0 runs before the update loop. It might be executed 0 to several times per frame to correctly calculate the physics simulations, when the delta time between frame updates is different than the fixed timestep defined in project settings. This means your physics interactions can become more rate-stable. Fixed updates on physics objects with your optional Lua fixed_update() callbacks in scripts are independent of the frame rate, so even if frame time fluctuates, you are ensured your physics simulation steps are not missed before next frame render.

Two important notes, before diving into details:

  • Input is handled before the whole Update Loop. on_input() belongs to the Input phase, which runs before Fixed/Update/Late Update.
  • Per-object order is not guaranteed for neither fixed_update(), update(), nor late_update(). Design as if objects could be updated in any order.

How to use Fixed Update?

A robust pattern for physics-based games might now look like this:

  • read input as usual (input is handled before update callbacks),
  • store intent (key inputs, axis movement, jump pressed, etc.),
  • apply the intent to collision objects inside the fixed_update() (force/torque/velocity, etc.).
  • do the rest regarding visual representation in update() and eventually late_update() if needed

With fixed timestep enabled, you can now treat fixed_update() as your consistent simulation step, while still keeping update() purely frame-driven. This should reduce the classic issues when physics behavior subtly changes across devices due to varying dt.

When to use Fixed Update?

Use the fixed_update Lua function for anything that must be stable at a fixed cadence, regardless of framerate:

  • applying forces / torques / impulses
  • setting velocities and accelerations
  • stepping deterministic movement that should not vary with frame pacing
  • feeding a physics-based controller from buffered input

Important to know: fixed timestep improves stability compared to variable dt updates, but it does not guarantee cross-platform “bitwise determinism” for a physics simulation (floating point and platform differences still exist), so remember about it, when making physics-heavy games.

How to enable Fixed Update?

Fixed Update is disabled by default. If you need to enable it, check those settings in your game.project file:

Update

Update is the regular, frame-timed callback. It didn’t change itself, so in Defold 1.12.0 it is called exactly once per engine frame, but note, it’s called after all fixed_update() steps (if enabled) and before late_update(). The dt you receive here as a parameter is the variable delta time since the previous frame, so this is the right place for logic that should naturally run once per rendered frame, not once per physics step.

When to use Update?

Do here everything else related to your game, that is needed to be called once every frame, like for example:

  • animation state machines
  • all visual components updates (sprites, models, meshes, etc)
  • positions, rotations and scales updates
  • UI updates
  • non-physics gameplay systems that should tick once per rendered frame

Of course, we strongly advise to only do here stuff that must be done each frame. If you are making a turn-based game, or any other system of your game is not needed to be run each frame, convert it into a reactive system with proper use of messaging system or input system.

Late Update

Late Update is new in Defold 1.12.0 and runs once per frame, after all update() callbacks. As noted, Defold dispatches in-game messages between all these stages, which makes late_update() Lua callback an ideal place for reacting after everything is settled. You can think of Late Update as your final pass before rendering. It happens at the end of the engine update loop, just before the render script is called.

When to use Late Update?

This is where physics-based projects can get an immediate quality bump, as following can be performed here based on final positions for the frame:

  • camera updates (following, shake, dead-zones, smoothing, etc.)
  • snapping visuals to physics bodies to reduce “one-frame behind” feel
  • last-pass transform adjustments (e.g. look-at, simple constraints, tweaks that should not feed back into simulation)

You can safely update game object transforms in late_update(), because components update internal transforms after script execution, and the engine performs a final transform update at the end of the update loop, if needed.

Do not use late_update() for simulation. If something must affect the physics step deterministically, keep it in fixed_update(). Use late_update() to make what the player sees match what the simulation already decided.

For more details, we advise you to study the Application Lifecycle manual that was updated to match the changes:

Examples

Recently @JuLongZhiLu shared an example for late update:

And it presents one of the most common usage of late updates - camera following the characters. So, when you make all your characters updates in update(), then simply in late_update(), where you are guaranteed, that all update()callbacks were already executed before, take the final position of the character (or multiple characters, if you want to center camera between them) and calculate the final position of the camera.

Here you can also check out a short, nice example to quickly detect fixed-step “bursts”:

function init(self)
    self.fixed_steps_this_frame = 0
end

function fixed_update(self, dt)
    self.fixed_steps_this_frame = self.fixed_steps_this_frame + 1
end

function update(self, dt)
    if self.fixed_steps_this_frame > 1 then
        print("fixed steps this frame:", self.fixed_steps_this_frame)
    end
    self.fixed_steps_this_frame = 0
end

This will print how many times per frame the fixed step update was called, so you can determine whether you’re hitting multiple fixed steps in a single rendered frame.

Interpolation

As @aglitchman presented, it’s possible to create a custom component that, when just added to a game object, interpolates transforms between fixed update states for smooth movement, and there is an open source project with it:

And this gives some really nice results:


Do you have any other tricks for physics based games? What’s else worth noting when making those?


P.S. Game from the top banner is “Bounce Ball” by WeDoYouPlay , you can check it out on Poki:

25 Likes

I was having trouble wrapping my head around when to use late_update. This is really useful and clears it up for me! Thanks so much for the write-up!

5 Likes

So how does this work for something like a Mario/Celeste/2D-Zelda style character controller where the dev wants more control over the character? Things where the character is not a dynamic physics body.
e.g. for characters whose movement is something like this:

local moveAmountThisFrame = direction * speed * dt
local newPosition = go.get_position() + moveAmountThisFrame
go.set_position(newPosition)

Previously I have been using Update() for movement like this.

2 Likes

When you are not using physics (collision objects components), then I think this is a good approach, no need for fixed update (late update benefit you can use to make better control over camera still).

3 Likes

me, when i saw my art on the main page of technical article from defold team.

4 Likes

Very good. I’m in favor of more topics like this.

3 Likes

I was going to ask that very question.

1 Like

Now that I’m reading the platformer question again (but on my pc…I just have a harder time comprehending on the phone screen…weird I know).

So how will this work with collisions with a kinematic player object? What are the best practices? Not in controlling the character but in responding to the physics collisions? Everything still in update()?

Reason I ask I still have some issues I need to address with my player/character controller for my metroidvania toolkit. I have lots of settings to make it feel different based on what style control you’re going for, but to me it still feels just a bit off. I can’t get that true Hollow Knight, Shinobi, Mario, Celeste feeling. Sometimes the redirection doesn’t register or it’s slightly delayed in response occasionally. The issue I have is it feels good then just one time it misses the button press or the direction of the stick moving.

I was hoping last update might be able to help.

1 Like

In your example, you are still doing physics: you are integrating position over time.

Any time you’re doing a physics integration, you’ll benefit from using a fixed update step instead of a dynamic time step (which you get in update). If you already have something in update, it’s easy to intuitively learn why by locking the framerate to 20, then locking it to 240. Your integration will run differently in each case, with the same code, but this is a common scenario in the real world. Imagine a user on a high refresh monitor playing your game, then their antivirus starts for a few moments and drops their framerate.

Moving all game-state impacting integrations to a fixed step will make the feel of your physics (even custom physics) stable across devices and across time. This is more important the more non-linear your physics inputs, since a non-linear simulation loses stability faster. For example, if you integrate gravity as well, then you’ve got a non-linear system (acceleration → velocity → position over time) that will fall apart without a fixed time step.

4 Likes

Yes, right, as Hunter explained above, and I think this answers better both questions, when you are involved into physics, it’s better to use fixed update, in your cases, you’re probably using kinematic + you’re doing your own calculations that shouldn’t be frame time dependant

2 Likes