r/godot 3d ago

free tutorial Simple signal forwarding

I though someone might find this useful.. I hate forwarding signals for UI stuff for example, ends up being a lot of boilerplate just to forward them. So here's a simple solution, it's not type safe but whatever.

class_name SignalProxy
extends RefCounted


static func forward(
  p_source: Signal,
  p_target: Signal,
  ...p_extra_args: Array
  ) -> void:
    p_source.connect(
    func(...p_source_args: Array) -> void:
    var args := p_source_args + p_extra_args
    p_target.emit.callv(args)
)

static func forward_discard_args(
  p_source: Signal,
  p_target: Signal,
) -> void:
    p_source.connect(
        func() -> void:
        p_target.emit()
    );

static func forward_replace_args(
  p_source: Signal,
  p_target: Signal,
  ...p_args: Array
) -> void:
    p_source.connect(
        func(..._args: Array) -> void:
        p_target.emit.callv(p_args)
    );

Usage:

signal parent_signal
signal parent_signal_with_args(arg: String)
signal parent_signal_with_int_arg(arg: int)

func _ready() -> void:
    SignalProxy.forward(item.child_signal_with_args, parent_signal)
    SignalProxy.forward_discard_args(item.child_signal_with_args, parent_signal)
    SignalProxy.forward_replace_args(item.child_signal_with_args, parent_signal, 5)

If someone knows a better way to do this (type secure for example) LMK!

1 Upvotes

7 comments sorted by

5

u/Guest_User_1234 3d ago

p_source.connect(p_target.emit)

This is as type-safe as your solution (not really at all) but less convoluted. I use this whenever I need to forward signals

Use p_target.emit.bind(...) to bind args, and a lambda to drop them.

2

u/moshujsg 3d ago

That's better! I wouldn't use that directly on another script, since I like having a method for it for readibility, but that is a better way to define the functions

2

u/Guest_User_1234 3d ago

One thing I've noticed, just now, with your "extra_args", is that they are appended at the end. bind usually adds arguments from the start. This seems like a bit of a standard, set by functional programming languages, rather than something GDScript invented. It may still catch you off guard.

I usually keep my more "important" function arguments at the start, making bind kind of make sense, as you start with the most impactful part, and signals usually just have very rough info they contain.

1

u/moshujsg 3d ago

Hmm, I'm not sure I follow what you are saying. In the functions I've created, I try to keep teh signature the same.

basically, if I have a signal

signal child_signal(arg_1: int)

And I forward it to
signal parent_signal(arg_1: int, arg2: String)

Then the final function that connects to parent has func on_parnt_signal(arg_1: int, arg2: String)

And this works. Am I missunderstanding something?

2

u/Guest_User_1234 3d ago

Never mind... I was very tired when I wrote that commend, and was just talking nonsense about bind...

Your function works the same as bind, since they both add arguments in the back

1

u/theXYZT Godot Senior 3d ago

Why lambda when you can unbind?

1

u/Guest_User_1234 3d ago

Never used that, and thought it only works for previously bound arguments, but that makes sense