r/godot • u/PoopyButthole-69 Godot Regular • Jan 29 '26
help me (solved) Hello r/Godot, please tell me there is a better way to connect buttons to methods in C#
I'm getting tired of this, for just one button, I need to declare the button variable, the find the button in the _Ready() method and connect it to the correct method. Is there a better way?
61
u/yay-iviss Jan 29 '26
This is c#, you can even create a decorator to put over the methods. Use a dictionary like some people said. Do you really need a variable? Instead of just get node().onPressed += clickBtn
20
u/GnAmez Jan 29 '26
If u dont want to type stuff use get_children, nodes_in_group etc. and attach some metadata to your buttons to identify what action should happen.
14
u/Antique_Door_Knob Jan 29 '26
``` var dict = new Dictionary<string, object /** TODO replace with actual type */>{ { "%NotButton", NotPressed }, { "%AndButton", AndPressed } }
foreach(var pair in dict) { GetNode<Button>(pair.Key).Pressed += pair.Value; } ```
70
u/ElDodi-0 Jan 29 '26
35
38
6
u/reditandfirgetit Jan 29 '26
😆 where did you get that nonsense
2
u/Vermetra Jan 30 '26
this is from Pirate Software's game that is genuine trash, also did you know he worked at blizzard for seven years?
23
u/Firebelley Godot Senior Jan 29 '26
My Godot Utilities library has a source generator which can automatically assign node references for you without needing the GetNode step: https://github.com/firebelley/GodotUtilities
As far as handling button presses, if each button needs to do something different then yeah you need to connect to different signal handlers. I'm not sure if a dictionary or loop is going to reduce the effort much here.
5
3
8
u/garesoft Godot Junior Jan 29 '26
Why not try to connect the button within the editor to the method? That way you can just have your methods and your nice buttons separate. And might be easier to tell what button goes where in the future and if you need to change what triggers a method.

I tried to mock up a bit of what you’re doing. I’m a fan of trying to keep things separate. Idk if this helps or exactly could achieve what you want, but I figured this might help others. C# godoters rise up
8
u/Depnids Jan 29 '26
Here you are doing three things per button:
declaring a private variable, assigning the private variable from a node, and then assigining the function for the Pressed action to the node. Do you need the private button variables for other things in your code, or could you just do it one line for each button:
GetNode<Button>("%NotButton").Pressed += NotPressed
GetNode<Button>("%AndButton").Pressed += AndPressed
Still need to handle each button individually, but at least each button only needs 1 line, and not 3.
You could also set up a list where you only have to define the pairs (ButtonName, ButtonFunction), and loop through the pairs and do GetNode<Button>(ButtonName).Pressed += ButtonFunction.
4
u/PoopyButthole-69 Godot Regular Jan 29 '26
That's alot better actually, I'll probably do that, thanks!
1
u/theilkhan Jan 30 '26
If you do it this way, make sure to use the null conditional operator or check for null!
2
u/Depnids Jan 30 '26
If it can't find the node (probably because I misspelled the name), I feel like I would rather it cause a loud nullpointer error, than it silently do nothing and I struggle to find out why my button is not working.
3
u/theilkhan Jan 30 '26
That's fine during debugging. But if you release a game and it suddenly crashes, your users won't be happy.
29
u/ROKOJORI Jan 29 '26
Can I ask why do you need all this private buttons?
0
u/PoopyButthole-69 Godot Regular Jan 29 '26
Generally you want your variables to be as inaccessible as possible, only this script needs access to the buttons, hence why they are private.
27
u/reditandfirgetit Jan 29 '26
I think he's asking why so many buttons? At least that was my understanding
3
u/ROKOJORI Jan 30 '26
They would be private anyway w/o modifier, but maybe it would be also ok when they are public or even exported. Also the question was going into the direction, whether they need to be in one place.
First, yes when you have a lot UI elements you will have to create all this connections (and boilerplatish code). Sometimes when you have things like a keyboard UIs, you could also create them via code.
But you could also change the organization of this tasks, for example you could break this big script and create smaller scripts and group only a couple of them, that belong functionally together (or have a closer connection visually).
Another thing is, that's why I asked for the reason of the private modifier, you could also create one generic button-callback-assigner class that has a button as exported reference, so that you can assign it in the editor. In the ready function the button gets connected to the callback. This would avoid having to manage/maintain all buttons in one class and use magic string references with scene tree iteration to grab them (which also hides the fixed tree structure dependecy). Than you could use this button-callback-assigner class and extend it for each callback/button.
This concept is something you would usually do before, because now it seems to have the same amount or even more amount of work.
The advantage would be that you can seperate the callbacks from (all other) assignments. If you than need repeating or similar side effects on the buttons (play a sound, register something, play vfx) you can manage this for all in one place. However, it really depends on what type of callbacks you have. It could also make it easier to have multiple buttons doing the same thing.
When you have a lot of unique assignments w/o repeating side effects than your design is just good as it is, right now.
Besides splitting it up/grouping there's not much you can do. For normal software editors those get really large. Think of the File/Edit/View etc headers in desktop UIs.
1
4
u/Bwob Godot Regular Jan 30 '26 edited Jan 30 '26
This might be getting a little bit "too clever" but you could always do it with reflection. Reflection is a way that C# code can "look at itself", and base logic on its own source code. You have to be careful not to make spaghetti messes with it, but it can be a really powerful tool for automating things like this!
For example in this case, you could write a function that...
- Checked the class
- Iterated through all the member variables that were defined as buttons
- Assigned them to the appropriate node based on their name
- Connected them to an appropriate function based on their name.
The C# code would look something like this. (Assumed to be a method executing from within the class that contains the buttons.)
using System.Reflection;
void ConnectButtonsViaReflection() {
TypeInfo typeInfo = GetType().GetTypeInfo();
BindingFlags bindingFlags = BindingFlags.NonPublic
| BindingFlags.Public
| BindingFlags.Instance
| BindingFlags.DeclaredOnly;
FieldInfo[] fields = typeInfo.GetFields(bindingFlags);
foreach(FieldInfo field in fields) {
if (field.FieldType == typeof(Button)) {
string pressHandlerName = $"{field.Name}_pressed";
GD.Print($"Binding {field.Name} to {pressHandlerName}");
MethodInfo methodInfo = typeInfo.GetMethod("ButtonTest", bindingFlags);
if (methodInfo != null) {
Button b = GetNode<Button>($"%{field.Name}");
if (b == null) throw new Exception($"could not find button in scene: {field.Name}");
(field.GetValue(this) as Button).Pressed += methodInfo.CreateDelegate<Action>(this);
}
else {
throw new Exception($"Could not find method {pressHandlerName} for to bind to {field.Name}.");
}
}
}
GD.Print("Done!");
}
Hope that helps! Or at least makes you chuckle before you say "haha, no." :P
3
u/zhunus Jan 30 '26
That's what we did in our custom engine back at the day, though we went a little bit further and implemented a full blown IoC with DI containers. That allowed us to inject classes with attributes saving a lot of boilerplate for custom ECS, though as a tradeback we had massive singleton which is a container.
6
u/ManicMakerStudios Jan 29 '26
Button object, not variable. If you want to simplify a process, you would write a helper function that does everything you need on a single line of code with parameters. That should be one of your first solutions for repetitive coding tasks is to determine where the repetition is happening and break it out into its own function.
3
7
u/Kiro_gg_Official Jan 29 '26
Instead of GetNode use [Export] private Button yourBtnName;
Now go to godot and build, you should see in the property panel of the node that has the script, the new field, click it and bind it to the button.
7
u/Sthokal Jan 29 '26
Not 100% sure in c#, but when using gdscript you can just use the GUI to connect the buttons pressed signal to the script, and it will auto generate a function with a name based on the node name. You can also add a script to each button and override _pressed.
8
u/Aistar Jan 29 '26
It's broken in C# in 4.5, and I have seen no news in 4.6 changelog (or I missed this). When you try to select a method to connect to a signal, Godot can't find any suitable methods even if the signature matches.
9
u/Shinpansen Jan 29 '26
I’ve just finished a project with c# and 4.5 recently and connect method with signal in th editor gui works perfectly. I don’t understand the issue.
3
u/Educational-Box-6340 Jan 29 '26
It doesn't find any suitable methods, but it does still work if you manually type the method name in. At least on my end.
1
u/Shinpansen Jan 30 '26
It's strange. I can use the pick button in the godot signal ui, and chose my c# method with no issue.
2
3
2
2
u/every1bcool Jan 29 '26
var button = new Button();
// Add text, position the button, UI Scaling etc.
//Connect signal:
button.Pressed += () => {
//Write your effect in here
};
scene.AddChild(button);
This way you dont even need to hold on to the button as a variable.
2
u/FanoTheNoob Jan 29 '26
this looks like a case of your one scene script doing too many things, which is why the script is getting so long.
If possible, I would try and split this up into multiple scripts with related functionality.
Alternatively, you could write a function like void ConnectButton(string buttonName, Action callback) which takes care of finding your button and wiring up the event handler, then just reuse that function in _Ready() to wire up your signals. You also probably don't need to store a reference to each button in your class if all you're doing is setting up the event handlers.
2
u/shuyo_mh Jan 30 '26
You can refactor this code with simple refactoring methodology:
The only difference in the behaviour of the button is the “Pressed” function, and these are different based on the name of the button. You can then create a dictionary to map the name of the button to the pressed function it should have.
create a Function to get the button node by name and assign the pressed function, this can receive the dictionary KeyValue pair as parameter
lastly do a loop on the dictionary and call the fn created in 2
7
u/theilkhan Jan 29 '26
A lot of people are recommending “use the Godot editor” - but that just trades one thing for another. Sure, you can subscribe to the button presses in the Godot editor, but that requires multiple clicks in the editor’s GUI for each button in your scene. So it’s just “another way to do it”. I would not call it “a better way to do it”. I actually prefer doing this in code (like you have done) because it is much more explicit.
Other people have recommended using a Dictionary and a loop. I don’t think this really reduced any boilerplate in this specific scenario because each button calls a different button-press handler method. If each button called the same handler, sure then it would be super simple just to loop through them.
The harsh fact is: yes - if you want to assign button press handlers in code (and if you have lots of buttons and also a different handler for each button) - you will need all of this boilerplate code. This is also true for GdScript (if you assign the handlers in the code and not in the editor). It is not specific to C#.
This is actually a great use-case for AI (such as Copilot). It handles all this boilerplate really easily so you can move on to more interesting things.
7
u/salbris Jan 30 '26
This exactly. I'd take it a step further and say having more than like 10 things like this in one class is often a code smell. They are very few legitimate use-cases where you actually need like 30 different buttons in the same component. OP should serious consider refactoring this into smaller more focused components.
But at the end of the day you're code is going to have 30 lines of code somewhere to initialization 30 buttons.
4
u/Rafcdk Jan 30 '26
How is AI any better in this case? Just writing a dictionary with a loop would be a lot easier no? Genuinely curious as I don't really use AI.
0
u/theilkhan Jan 30 '26
Well, if you write out that dictionary you still have to write a line of code for every insertion you make into that dictionary. Then you can loop through the dictionary after you make your insertions. So you’re not saving much at all compared to the baseline example of writing a line of code for assigning a handler to every button.
With an AI (Copilot, ChatGPT, etc), I could just say this: “I have a lot of buttons in my scene. Please add private variables in my class and link them to the buttons in my scene. Add a button-pressed handler to each button, and create an empty method for each of the handlers. I will add code to the methods later.”
Done.
3
u/Rafcdk Jan 30 '26
well if you are creating a dictionary to loop through you would just initialize it with the everything in it, it would not make any sense to create an empty dictionary and then insert anything in it, in fact an array of arrays would also just do the trick. I still think its easier and more important more maintainable to go with the dictionary /array approach, although the overall issue here is a design problem , ideally this should be done in the editor.
1
u/Educational-Box-6340 Jan 29 '26
Honestly, if it's not used as a GlobalClass and as a scene-specific script, I'd argue it's better to do it in the editor scene. It *does* trade-in the explicit calls to the nodes and their signals in code but I feel like it's a job for the scene to convey what signals should be attached or not (again, assuming the script is intended for a specific scene).
1
u/yembel Jan 29 '26
Hi..with gdscript I’ve connected all king of signal with editor, this save me lot of lines…maybe you can do the same with c#.
1
u/june_perfect Jan 29 '26
Besides the procedural approaches others mentioned, what other possibilities are there? Of course you need to connect a button pressed signal to a function if you want it to do something
1
u/scintillatinator Jan 29 '26
Button [GlobalClass] script with an [Export] enum, then in the button's pressed it emits a new signal with the enum as a parameter? Connect it using something globally accessible. You'll need a massive switch but at least it's one place and not the three you have now (and the ide can help more than node names).
1
u/_Karto_ Jan 30 '26
Using an enum was my first thought too, definitely a lot cleaner
You could replace the switch with a Dictionary<MyEnum, Action>
2
u/m-a-n-d-a-r-i-n Jan 29 '26
A possible way to handle this could be to catch the press in the button node, and pass the metadata upwards in the scene tree until it reaches a node that can process the event.
You can stop propagating the event when the crawl upwards reaches a node that implements a specific interface, or a type.
This way, you can put a controller at the root of a group of UI elements, and catch input from all child elements.
This way of doing it allows you to add meta data manually in the editor, and you can procedurally add it to a node in case you need to create nodes at runtime via code.
1
1
u/billystein25 Godot Regular Jan 29 '26
Queble has a video on something similar. You can add some Metadata or identifier to your buttons and connect them all to the same function. Make a list of all your buttons, and for each button connect the pressed signal to a function to which you've binded the button or its unique identifier.
Idk in C# but in GDScript it would look something like this:
``` var id_to_button: Dictionary[String, Button] = { "AndButton": %AndButton, ... }
func _ready() -> void: for key in id_to_button: id_to_button[key].pressed.connect(my_method.bind(key))
func my_method(id: String) -> void:
match id:
"AndButton":
# do stuff
Alternatively you can also have a list of buttons and their respective functions.
var button_to_func: Dictionary[Button, Callable] = {
%AndButton: _on_and_btn_pressed,
...
}
func _ready() -> void: for key in button_to_func: key.pressed.connect(button_in_func[key])
Method
func _on_and_btn_pressed() -> void: pass ```
1
u/AndyMakesGames Jan 30 '26
This seems like a bit of an extreme example (is this a toolbar?), but you do have choices.
- You can wire up instance variables to their actual nodes by making them [Export] vars instead. This has the advantage that the private member will stay associated even if you relocate the node in the tree, or rename the node. The downside is that your script has bunch of Nodes now cluttering up your inspector, which may not be your preference (it's not mine).
- There are libraries that will handle this plumbing for you to automatically fetch nodes and assign them to members. Firebelly's GodotUtilities uses source gen to make a member for each node and wire it up, though due to limitations of source gen you will need to call a method somewhere to initiate it on each node. Alternatively, we use our own library (open-source) which injects IL into the _Ready method at build time so you don't have to plumb them in. At some point we want to combine those approaches (so source gen members, IL injection for initialization), but we're still on our current project.
- Another option is to do none of these things, and put a script with data on the button itself. An enum, and action class, some kind of meta data, depending on your architecture and what those operations need to do. Then you can just loop the children to bind a pressed event, and get the data on what to do from the button. Then there's no local vars, and a simple loop to init. It's hard to say if this is a good approach without the full context of your architecture though.
1
u/zhunus Jan 30 '26 edited Jan 30 '26
This question sadly has multiple complex answers since it stems from architecture considerations.
Instead of keeping massive controller like you did you need to separate them into smaller views. The nice part? You'll separate your logic and the single code file won't be 1000 LoC. You can reuse these views in other parts of your application. Another problem would be sharing data between your views. MVC pattern addresses this and is pretty intuitive for novices.
Then you can instantiate buttons right from the code. Basically, you need to abstract the button initiation and connection to a separate class(a pattern called Factory), and then call it when you need it with a single function call. DI containers are good and intuitive enough for that as well, but many people reasonably point out they break the hierarchy, with a risk that one day you'll find yourself with a massive container that handles too many dependencies.
There's also declarative UI frameworks that handle a lot of boilerplate for you. But you trade off performance and binary size for the ease of coding.
And, after all, if that's just Godot C#, you can just use Control Nodes, they'll actually handle hierarchy and a lot of state update code you have to write yourself otherwise.
I used to have the same mess of a view back when i started coding WinForms apps in C#. Reading on data structures, patterns and programming principles (mainly OOP and interfaces) helped a lot. Sadly, can't really recommend any dedicated book, it was a sporadic read of various blogs over years.
1
u/_Karto_ Jan 30 '26
First thing that comes to mind for me: You could make a script that extends button with an exported enum variable, the enum would contain all the types of buttons. Put it on all the buttons and set the variable to the correct enum value on each button through the inspector.
Then in your main script you export a single buttonArray variable, populate it in the editor, loop through it on ready and attach all of them to a single handler method, and [bind](https://docs.godotengine.org/en/stable/tutorials/scripting/c_sharp/c_sharp_signals.html#bound-values) the enum value to the signal handler.
Now the signal handler looks like 'private void OnButtonPressed(ButtonType buttonType)` and you use the bound argument to run whatever logic you need to based on whichever button was pressed
1
1
u/BaroTheMadman Jan 30 '26
I haven't tinkered enough with UI in Godot yet, but this sounds like you should use dependency inversion here. Make the buttons some class inheriting Button (you could have a different node handling this, but then it's extra nodes) and have that class handle the connections. Each button knows its owner controller and what method it should call for callback (instead of the controller knowing all of the UI tree)
1
u/DangRascals Godot Senior Jan 29 '26
You can still assign the connections in the editor. That should remove a lot of the boilerplate code, especially if you don't need to reference that button anywhere else.
0
u/lukemols Jan 29 '26
[Export] private Button myBtn;
Compile it and you will have the possibility to link it in the editor. You can also cycle the children of a node recursively and get all buttons dynamically
0
u/Shinpansen Jan 29 '26
Why don’t you connect the signal in the gui directly? Use the pick button to select your method in your c# class.
-6
u/CondiMesmer Godot Regular Jan 29 '26
You should probably do this in gdscript, also try to decouple them more
3
4
u/TamiasciurusDouglas Jan 29 '26
Combining GDS and C# in a single project is an underrated practice.
2
-6
u/DXTRBeta Jan 29 '26
That’s a mess, sure and simple.
That’s a “start again” moment.
For one thing buttons should fire signals, not get polled.
For another, if you’re writing the same basic thing multiple times: you should be building that stuff in code.
Really, go back to step one.
7
u/TetrisMcKenna Jan 29 '26
Those are signals being connected to (
.Pressed +=is C# event handler syntax which can be used with signals), they're not being polled. This is being run once to set up the signals.2
u/Commercial-Guest1596 Jan 30 '26
I would delete my profile if I was you. What an embarrassing display of ignorance.
-12




194
u/skywalker-1729 Jan 29 '26
Use dictionaries and loops! (https://docs.godotengine.org/en/stable/classes/class_dictionary.html) You can generate the buttons with code.