Skip to content

Godot Signals

PixelRain edited this page Dec 27, 2024 · 9 revisions

A godot signals example:

bind.hpp is the main translation unit in charge of interfacing with the godot API for signaling. The following steps take you through roguelite's code, with the stated file where each code snippet can be found, and what it does.

  1. constants.hpp Define an event name as a string
namespace event
{
    constexpr inline auto character_shoot{ "character_shoot" };
}
  1. character_controller.cpp The following code adds a signal binding. Basically the place where functions can be inserted for being called when the signal is emitted.
void CharacterController::_bind_methods()
{
    signal_binding<CharacterController, event::character_shoot>::add<godot::Object*>();
}	

Might be neater to do this in the header file, but it's a source file example here.

<CharacterController, is the name of the node class which we will add the binding to.

event::character_shoot> is the name of the event which we defined in step 1. If this event name is emitted from an emit_signal, any callback methods bound to this binding through signal will be called.

add<godot::Object*>() is a <typename... TArgs>();, a succession of method arguments callbacks attached through signal to this binding expect to receive.

*This signal binding roguelite example won't be visible in editor inspector since it's created and bound at runtime. And can't be seen in runtime remote view either since godot disables the inspector node tab at runtime as of writing this.

For an example of custom bindings which ARE visible, check out the inspector node tab for Player.tscn's root node, which has the position_changed and spawn_projectile (character.hpp) custom bindings visible for demonstration purposes.

  1. character.hpp Define a method as [[signal_slot]] (signal_slot is just a custom attribute for ease of identification). This method is the one that "subscribes" or is "called back" by the signal.
protected:
    [[signal_slot]] void on_character_shoot();

Don't forget to also implement it of course, with whatever the received signal should trigger.

  1. character.hpp Bind the member (callback) function to the godot engine, this makes it accessible from the engine (runnable from gdscript, attachable to signal bindings...)
void CharacterController::_bind_methods()
{
    bind_member_function(Character, on_character_shoot);
}	

Might be neater to do this in the header file, but it's a source file example here.

  1. character.cpp The following code binds a function to the previously defined signal binding. In godot you would select a node and method when double clicking the binding from the inspector. This is the same, but through code.
_ready()
{
    signal<event::character_shoot>::connect<CharacterController> 
    (m_character_controller) <=> signal_callback(this, on_character_shoot);
}

As you can see, the signal template argument <event::character_shoot> defines which signal event we're talking about, through it's string event name.

The connect member function <CharacterController> template argument defines the class type of the object where the binding (declared in step 3) is located (essentially the signal owner).

We also need a reference to the object itself where the binding is created, in this case m_character_controller.

In custom cases you are free to determine how you will reference the node with the bindings. Some ideas would be referencing with a property bind from bind.hpp, or binding a C++ method manually through the actual godot inspector. Both are done from the godot inspector and require the node to be instanced in the same scene though.

The signal_callback part receives two arguments, the first is the class for the callback method (the method that will be called when the emit_signal function is called further on from player_controller.cpp), and the actual function name, in this case on_character_shoot which we defined in character.cpp on step 2 as [[signal_slot]].

  1. player_controller.cpp You can now call emit_signal, This method has to be called from the same node where the binding for the signal being emitted was set in step 2. In this case the signal event is event::character_move and triggers all callbacks attached to the signal binding.
this->emit_signal(event::character_shoot);

Transfering information through signals:

This is also a "faster guide" if you wish to ignore all of the details above. It's actually possible to transfer information through signals:

character.hpp The callback member function accepts a godot::Vector2 and double:

[[signal_slot]] void on_character_movement(godot::Vector2 movement_velocity, double delta_time);

character.hpp Member function registration for the engine:

static void _bind_methods()
{
    bind_member_function(Character, on_character_movement);
}

character_controller.cpp Binding:

signal_binding<CharacterController, event::character_move>::add<godot::Vector2, double>();

character.cpp Signal connect and callback:

signal<event::character_move>::connect<CharacterController>(m_character_controller) <=>
	signal_callback(this, on_character_movement);

player_controller.cpp Signal emit:

this->emit_signal(event::character_move, velocity, delta_time);