Game + Physics time steps BETA testing

Well, the physics is the main reason for the fixed update function. The question was, what other uses there are?

3 Likes

I had cases when I wrote my fixed update for validation on the server. The game recorded certain events tied to the counter and then, roughly speaking, played the game on the server.

5 Likes

Network games need it. Since with deterministic update on client and server traffic between then can be dramatically reduced. Same for p2p games. We have our custom fixed update for this

5 Likes

Thanks!
The server updates are a good use case.

I wonder about the frequency you use in these cases?
E.g. compared to physics updates which might use 60 or 30 Hz.

3 Likes

Hmm, if you’re asking if it should be part of the physics module, I think fixed_update should exist even if the engine is compiled without a physics engine. Someone might want to implement their own physics and fixed_update is still useful in that scenario. (though concievably, one could implement the behaviour of fixed_update() using update(), it’s just more work and an extra thing to pay attention to)

As for non-physics related things, you can simulate so many non-physics things: Think Factorio, for example. Each of their tiles have particular behaviours they do each tick and ideally that shouldn’t be tied to the framerate. Or events and troop movements in a military simulation, etc…

Can these be done in a FPS-independent way using just update()? Probably yes, by implementing a similar logic as you have and calling your tick more/less times per frame depending on dt.

Is it good to have a convenience function for this? Definitely and I will be switching some simulation code for our tower defence mini-game in GMAI to fixed_update() if it gets implemented. Getting that timing behaviour right without stutters is not an easy task and I think it’s good if you give Defold users a “known good” implementation of it.

One place where fixed_update() is really necessary and no other alternative exists, is when wanting to sync up with the built-in physics engine.

7 Likes

Thank you for your input!

We’ll go for adding a new life cycle function fixed_update(), and we’re currently trying it out a bit, to better understand what the migration steps will be for our users.

5 Likes

We use 10 Hz. But i think it is ok to fix it on 60Hz, and it can be resampled down by project individually.

3 Likes

Could fixed_update() help fix the examples above? In my project, visuals, including camera movement, lagging one frame behind is one of the recurring Defold issues I haven’t been able to solve in a good way.

2 Likes

Thanks!
I guess having it slower than the physics is easier. E.g. if the fixed frequency is a 60Hz, you’d call your server sync every 6th frame.

@totebo
We’ll most likely add another life cycle function for the post update. I’m hoping next version (after this one)

4 Likes

No, it couldn’t. We have the same problem. I would love to have the late_update(self, dt) function in scripts or an option to subscribe for the late_update message (for example, i.e. any option is OK).
Also, I’ve found that native extensions can subscribe on the pre-render callback and it can be used for late update logic (but, unfortunately, messages from that logic will be dispatched next frame). Because, broadly speaking, the purpose of the late update is to prepare your scene for rendering, i.e. move/rotate the camera or other objects that rely on the transformation of dynamic bodies. I’ll post the results of my experiments then.

Sounds great!!

4 Likes

This is really exciting. It will make a huge difference to my workflow.

1 Like

I’m of the same opinion.

I saw that some developers use fixed update for animations. However, animating something in the fixed update can produce visual jittering imho and should be used with caution.

I stumbled upon recommendations to use fixed update only for updating physics. For networking, they say that it’s better to implement your own fixed-step function for better control, i.e. as @Sippul did.

If you are asking that to consider whether to introduce a new function to scripts or implement that via messages (for example, to send physics_update message from a collision object to a script) then we love to have any of those options. Personally, we will use it only for physics now and will test it on Puffy Cat - the game requires the fixed step for physics to not to fall through platforms.

2 Likes

I believe a fixed update amount would help with timing on a rhythm game I’ve been toying with. This would allow for time aligning much easier.

1 Like

Not sure if this is the right thread @Mathias_Westerdahl, but I’m experimenting with the fixed_update in our current project (using the Defold version v1.3.1).

When using fixed_update to add force, the game object seems to stutter while moving. While moving it to a regular update instead, it moves smoothly.

Ex:

go.property("turn_amount", 30)

local left_turn = nil
local right_turn = nil

function init(self)
  left_turn = vmath.vector3(self.turn_amount, 0, 0)
  right_turn = vmath.vector3(-self.turn_amount, 0, 0)
end

function fixed_update(self, dt)
  local turn = vmath.lerp(delta, left_turn, right_turn)["x"] -- no linear lerp function i defold except in vmath
  local turn_quaternion = vmath.quat_axis_angle(vmath.vector3(0, 0, 1), turn * dt) -- set the turn quaternion based on the turn value
  local rot = go.get(".", "rotation") * turn_quaternion -- multiply with the current rotation
  go.set_rotation(rot)

  local forward_speed = 1
  local distance = forward_speed * dt
  local direction = vmath.rotate(rot, vmath.vector3(0, 1, 0)) * distance -- get a direction vector

  pos = go.get(".", "position")
  pos = pos + direction -- add this to the current position

  msg.post(".", "apply_force", {force = direction * 100, position = pos})
end
1 Like

Please assemble a small repro project that we can test!

Sure think, coming up :slight_smile:

2 Likes

Okay @Mathias_Westerdahl, manage to sort of put a bare case together. Striped our current project down as much as I could (a car game), but still some prototype code in there (and removed steering for easier testing).

But if you start the game, touch anywhere on the screen, the player will start moving forward with a (sometimes) stuttering movement. Seems to be performance related. Happens maybe half of the time on my machine (Surface Pro 8) and every maybe 4s on my mobile (Samsung Note 9, both HTML5 and Android build).

If you change fixed_update in touch_controller.script to update it should look better, but not perfect either. So is probably related to my code. I need to run some profiling on it and get back.

1 Like

I looked into your project and I see that you use collection proxies as “screens”. In my opinion, the issue is the same as described there - Defold 1.3.1 BETA - #51 by aglitchman

3 Likes

Seems to be the problem yes (I’m using the monarch plugin that uses proxies to handle scene transitions). When running the game directly to the in game scene, it works fine.

2 Likes

So, I’ve implemented the temporary solution. I’ve found that native extensions can subscribe on the pre-render callback, and I’ve used that for the late update logic. The only downside is that the callback isn’t called if the application is iconified.

How to use:

  1. Add Scene3D as a dependency: https://github.com/indiesoftby/defold-scene3d/archive/refs/heads/main.zip.
  2. Call scene3d.prerender_register/scene3d.prerender_unregister to subscribe (unsubscribe) for that pre-render event:
-- This update will happen after game objects have been moved by the physics engine
-- Messages from that code are dispatched next frame!
local function late_update(self)
    -- Get position/rotation, update position/rotation, etc.
end

function init(self)
    local priority = 1 -- It's an optional parameter. All pre-render callbacks are sorted by priority (ascending: 1, 2, 10, 50, and so on.) and called one by one.
    self.prerender_id = scene3d.prerender_register(late_update, priority)
end

function final(self)
    if self.prerender_id then
        scene3d.prerender_unregister(self.prerender_id)
    end
end
6 Likes