godot
godot copied to clipboard
(C#) .Bind missing on the Callable object
Godot version
Godot Engine v4.0.beta1.mono.official.4ba934bf3
System information
Windows 11, Vulkan API 1.2.0 - Using Vulkan Device #0: AMD - AMD Radeon RX 5700
Issue description
The method .Bind does not exists in the current beta, along with some of the docuemented code such as the .Connect on a delegate. I'm unsure if this is due to the fact that the documentation is regarding latest, and not beta, but as of now I can not see a way to connect a signal with additional paramteres.
Steps to reproduce
Create a new callable, and try to call .Bind, or any other form of passing a variable on build in signal
Minimal reproduction project
No response
This part of the documentation is still outdated, I have an open PR (see https://github.com/godotengine/godot/pull/64930) to fix this but it's still a draft pending some changes to Callable
.
For now, you can use lambdas to connect to signals passing additional parameters:
public class MyNode : Node
{
[Signal]
public delegate void MySignalEventHandler(int a, int b, int c);
public override void _Ready()
{
MySignal += MySignalCallback;
MySignal += (a, b, c) => MySignalCallbackWithAdditionalParams(a, b, c, 1, 2, 3);
}
private void MySignalCallback(int a, int b, int c) {}
private void MySignalCallbackWithAdditionalParams(int a, int b, int c, int d, int e, int f) {}
}
About ".Connect
on a delegate", this is not possible in C#, I apologize for having invalid syntax in the documentation. My recommendation is to use the generated events instead as shown in the example above. Otherwise, you'll have to use the .Connect
method on the node and pass the name of the signal and a Callable
instance:
public class MyNode : Node
{
[Signal]
public delegate void MySignalEventHandler(int a, int b, int c);
public override void _Ready()
{
// This way of constructing Callables will change in the future
var callable = new Callable(MySignalCallback);
Connect(SignalName.MySignal, callable);
}
private void MySignalCallback(int a, int b, int c) {}
}
That makes sense, the documentation was a bit odd. Your code works fine for signals that I create, but if I were to try the same with a built-in signal, such as Timeout, it does not accept additional parameters. Is there any way around this for now?
E.g.
public partial class Node2ds : Node2D
{
[Signal]
public delegate void MySignalEventHandler(int a, int b, int c);
public override void _Ready()
{
var _t = GetTree().CreateTimer(1);
MySignal += (a, b, c) => MySignalCallback(a, b, c); // all good
_t.Timeout += (a, b, c) => MySignalCallback(a, b, c); // no good
}
private void MySignalCallback(int a, int b, int c) { }
}
@TandersT: You can't add additional parameters to default signals, it's the same in GDScript
Okay, so as of now is there any way to achieve something similar to this, in which the variable _fire, is passed with the signal?
timer.Connect("timeout", this, nameof(FreeFire), new Godot.Collections.Array { _fire });
@TandersT I think you misunderstood my example, you can add additional parameters by using closures, see this example:
var _t = GetTree().CreateTimer(1);
_t.Timeout += () => MySignalCallback(1, 2, 3);
// Or capturing variables:
int a = 1;
int b = 2;
int c = 3;
_t.Timeout += () => MySignalCallback(a, b, c); // a, b, c are captured by the closure
To answer your last comment:
timer.Timeout += () => FreeFire(_fire); // The _fire variable is captured by the closure
@Zireael07 I'm pretty sure you can use bind/unbind with GDScript for default signals, unless I misunderstood what you meant:
func _ready() -> void:
var t := get_tree().create_timer(1)
t.timeout.connect(_on_timeout.bind(1, 2, 3))
func _on_timeout(a: int, b: int, c: int) -> void:
pass
Thank you, that does the trick! That's on my end, I see I misunderstood :)
@raulsntos Wow, TIL - this trick is worth documenting in the docs!
Should also be documented in Callable documentation itself, and possibly signals
@Zireael07 Sure. I just added C# examples where there were already GDScript ones. The Callable
doc page doesn't provide examples for bind
in any language at the moment. And pages outside the class ref are outdated on so many levels...
It's currently documented in Object's connect
method (a bit outdated, see https://github.com/godotengine/godot/pull/64930):
https://github.com/godotengine/godot/blob/aa553f403099a31520ab0c75a43f352642170d5f/doc/classes/Object.xml#L172-L306
But it should probably be documented in Callable
too.
Missed that one while reading through the docs. I can update those if you need me to 🙂
I already update it in https://github.com/godotengine/godot/pull/64930, marked as a draft pending some changes to Callable on C#'s side.
@TandersT I think you misunderstood my example, you can add additional parameters by using closures, see this example:
var _t = GetTree().CreateTimer(1); _t.Timeout += () => MySignalCallback(1, 2, 3); // Or capturing variables: int a = 1; int b = 2; int c = 3; _t.Timeout += () => MySignalCallback(a, b, c); // a, b, c are captured by the closure
To answer your last comment:
timer.Timeout += () => FreeFire(_fire); // The _fire variable is captured by the closure
@Zireael07 I'm pretty sure you can use bind/unbind with GDScript for default signals, unless I misunderstood what you meant:
func _ready() -> void: var t := get_tree().create_timer(1) t.timeout.connect(_on_timeout.bind(1, 2, 3)) func _on_timeout(a: int, b: int, c: int) -> void: pass
How to remove such a lambda bind from the signal after it was added?
How to remove such a lambda bind from the signal after it was added?
Do you mean disconnect the lambda from the event? It works like it does in C#, you need to store the lambda:
// Store the lambda in a variable
var myCallback = () => MySignalCallback(1, 2, 3);
// Connect the callback to the signal
MySignal += myCallback;
// Disconnect the callback from the signal
MySignal -= myCallback;
Keep in mind, the events we generate with source generators don't generally need to be disconnected because on disposing the object we will automatically disconnect all signals, if that's what you are worried about:
https://github.com/godotengine/godot/blob/f6f8a48459f9bbe97ee76a7186b9ae37e71e724b/modules/mono/csharp_script.cpp#L1743-L1745
The method described here fixed my issues, and since this Callable seems to have its docs updated, hence closing this issues.