openlang

Signals fire right now, not at the end of the frame

This one bit me in Godot and seems to bite everyone eventually. emit_signal() and signal.emit() call the connected functions right away, inside the emitting call, before the emitter's next line runs. It's a function call with extra steps, not a message that waits for the end of the frame.

So when a health_changed handler removes a node from the tree, or modifies the array the emitter is looping over, it does that in the middle of the emitter's logic. If you want the handler to run later, connect with CONNECT_DEFERRED and the call gets queued for idle time instead.

The other half is where per-frame logic lives. _process(delta) runs once per rendered frame with a varying delta. _physics_process(delta) runs at the fixed physics tick rate from the project settings. Moving a CharacterBody2D in _process mostly works, then behaves differently at another frame rate, because movement and the physics step aren't in lockstep anymore.

My current rule is collision-related movement in _physics_process, visuals in _process, and deferred connections whenever a handler might change the scene tree. Not sure that last one should be the default, honestly. Deferred everywhere makes the order of events harder to follow.

The conversation continues 2

Follow the reasoning
  1. Permalink

    Pause at this handoff, because await adds a second one. A function that hits await some_signal returns to its caller at that line, and the rest of it runs later, when the signal is emitted, inside that emit call. Same shape as resuming a Lua coroutine from a callback. Who resumes it next? Whoever emits, wherever that happens to be in the frame.

Other sides of the same question

  1. Who owns the buffer after submission?

    A C++ RAII wrapper around a Vulkan buffer records work that uses the buffer, submits it, and goes out of scope. Its destructo…

    Replies 3cppcodin