Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- https://www.reddit.com/r/godot/comments/4n8rts/explain_to_me_how_to_use_the_signals_in_godot/
- Explanation Signals are a bit like following someone on Twitter (this is a bit stretched, but bear with me). They might tweet 5 times a day, but if you aren't following them, you won't see those tweets. When someone tweets, Twitter looks up all that person's followers and sends the tweet out to them. Those that aren't interested (aren't following) don't get the tweet. By following the person, I'm saying "I'm interested in whatever they are tweeting, please send it to me".
- Signals are a bit like that. A node can 'connect to' (like 'following') a signal (tweet) in which case they will be notified (a function will be called) whenever a signal is 'emitted' (a tweet is posted). Many nodes can be connected to a signal, like many people following a Twitterer, and they will all get notified about the signal. Now for an example.
- Godot's pre-existing signals
- Example of the Timer class. It has a signal "Timeout" that is 'emitted' (sent/broadcast) when the Timer's countdown has finished. It will emit this signal even if nobody (no code) cares about it. But if you have a Timer in your project, then you would probably care about it. So, we 'connect' our code to the Timeout signal (in other words, instruct our code to listen for the Timeout signal). How?
- Assume you have a Timer node in your scene
- Via the Godot editor GUI
- Click on the Timer node
- Click on the 'plug' icon at the top of the "Scene" tab (this is for 'connecting' a signal with a function)
- Find the "timeout()" entry (under "Timer")
- Choose one of your nodes. See under "Method In Node:", this is the function that will be created for you to accept (listen for) the signal
- Click "Connect" at the bottom and that function will be created in the node code
- Put some code in that function that does what you want, and Godot will call that function when the signal (Timeout) happens You can have more than one function connected to this signal.
- Directly in your code
- Instead of connecting via the editor GUI, you can do the same thing in your code. Same result as above.
- func _ready():
- timerNode.connect("timeout", self, "on_timer_timeout")
- func on_timer_timeout():
- # do your stuff here
- Your own signals
- You might want to create your own special signals. Why? Let's say you want several nodes to react when a game is over. You might create a signal "game_over". At the appropriate time, you call "emit_signal()" with your signal name as the argument. Godot will call all the functions that are connected to the signal (see below).
- # gameNode
- signal game_over
- func _ready():
- # stuff
- func character_died():
- emit_signal("game_over")
- Here we have a class (node) that is interested in the signal game_over, so we call connect() on node where the signal is declared (1st arg), tell it in which class/node the function is, in this case 'self' which is the class we are in (2nd arg) and also the name of the function to call (3rd arg). Similar thing for 'class 2' below.
- # class 1
- func _ready():
- gameNode.connect("game_over", self, "on_game_over1")
- func on_game_over1():
- # do your stuff here
- # class 2
- func _ready():
- gameNode.connect("game_over", self, "on_game_over2")
- func on_game_over2():
- # do your stuff here
- You can even pass parameters from the signal to the function, e.g:
- emit_signal("game_over", score, health)
- then elsewhere..
- func on_game_over(score, health):
- print("score: " + str(score))
- print("health: " + str(health))
- This is mostly from memory so I probably left details out, but that's the general idea.
- permalinkembed
- [–]warlaan 4 points 2 days ago
- This is a great explanation. I'll still add the image I usually use to explain signals because sometimes it helps to have something explained twice in different words.
- Imagine you have a cooking recipe. They always follow the same form: they start with the ingredients and then a list of tasks you have to perform. Basically that's what procedural programming is. You start by defining some terms that are specific to your situation and then you append a list of commands that somehow change the state of your environment. If you think of the recipe again it just assumes that you have a pan, a bowl etc. and most recipes don't even mention the cake or whatever it is that you get out of following the recipe. The cake is just a "byproduct" of following the recipe. In programming this is called a "side effect". So procedures are mostly sequences of commands that are performed for their side effects.
- The other extreme are functions. You may remember from maths class that functions get something as input (usually x) and give you something else as output (usually y or f(x) ). They don't have side effects, no matter what you write in a function, you will never be able to say "How much is f(10)?" and get a cake out of it. So a function only returns something, it doesn't affect the state of the environment.
- Object oriented programming is something in between the two. The idea is that an object may alter its own state but it shouldn't affect its environment. This may sound very theoretical and strange, so let me illustrate it with an example. Procedural programming is like a cooking recipe, so it's a very common way of describing a task. Object oriented thinking on the other hand is just as common even though it may not seem so. If you ask someone at work how he got there they will most likely not answer by telling you a sequence of actions they performed, they will probably say something like "by car" or "by bus". The car or the bus are now objects that wrap the whole procedure of traveling.
- This is not only helpful in order to describe how you got to work, it also allows car manufacturers to build an object that enables you to travel, even though the manufacturer has no idea where you live and what you have to do to get to work. This is possible because every detail that is necessary to travel by car is "encapsulated" in the car. All you have to do is use the "interface" of the car and "attach yourself to it" so when the car alters its own state (its position, the amount of fuel it has etc.) your state is affected alongside (you are transported to your destination).
- Now this works fine for a car, but sometimes it's not really possible to encapsulate everything into one object. For example if you want to watch a DVD your TV needs to receive the signal of the DVD player. That's where Godot's aptly named signals come into play. In real life the DVD player manufacturers simply defined a place where the player emits a signal that can then be connected to a TV screen. If you want to create an object that interacts with its environment you simply have to do the same thing: - define a signal (using the "signal"-keyword) - connect it (using the "connect"-command either in GDScript or in the editor interface) - emit the signal (using the "emit_signal"-command).
- The simplest case is an interface element. The point of a button is to trigger something when it is pressed, but at the time when Godot's button class was written the developers could not have known what it is that should be triggered in your game. So instead they added a signal that you can connect and they wrote the button class so that it emits that signal when the button is pressed.
- A more complex example would be creating a player that can shoot. In order to be able to test and reuse your player you should put it in a scene of its own. When the player shoots it needs to create a new shot object and attach it to a node that doesn't move. If the shot is attached to the player it will keep moving with the player, so that wouldn't work, but if the player is in a scene of its own you can't just use 'get_node("..").add_child()' because then you wouldn't be able to test the player scene anymore. It would crash since the player doesn't have a parent as long as it is not instantiated in a larger scene. So what you do instead is add a signal that is emitted when the player shoots and that contains the new shot as a parameter. That way you can connect the player's signal in the surrounding scene just like you would connect a DVD player in a living room.
- permalinkembedparent
- [–]Duke_meister 2 points 2 days ago
- No problem. I didn't want to sound patronising but the tone of the OP sounded to me like 'what's the story with signals' so I went for a simple approach. Always helps to have multiple angles on things :) Not sure I would've gotten into OO though, as (IMO) signals (asynchronous vs synchronous) and OO are really orthogonal concepts. Hopefully the OP will come back and have a good read and be able to piece things together.
- permalinkembedparent
- [–]batmanasbfollow @batmanasb 1 point 1 day ago
- Oh wow, so that's the proper way to spawn stuff in the world! I've been getting too messy with get_node() paths, but signals are definitely the better solution. Thanks for the examples.
- permalinkembedparent
- [–]Geaxle 3 points 2 days ago
- I was also trying to learn how to use this yesterday and by playing around I think I got the hang of it. I have to say the wiki on the subject is lacking for beginners.
- What was bogging me is where do you put the signal, where do you call it and all that.
- What you wrote from memory is pretty good. I'll jsut add what I was asking myself.
- the signal declaration and the signal calling HAVE to be in the same script.
- the signal connect can be in ANY script in the game but the function that you call HAS to be in the same script where you have the connect thing.
- you can connect as many script as you want, all having their own function as a result that will be called in the signal.
- if you want to have arguments passed over with your signal, follow the wiki. Just know in your connect, you do not have to put the arguments, and you don't even have to use them in the called functions. But if you have several arguments and only want to use 1 in the called function, then you have to mention ALL arguments in order.
- don't forget that your argument and function have no quotes, but you have to put them in the connect thing.
- Example: I have a node where the ocean can rise and so I emmit a signal "water_rise" with the arguments "water_height" and "water_volume". I have a script that updates the text with the new height of the water on the UI. So when I receive the signal, my function only needs "water_height", but I still have to put as argument both "water_height" and "water_volume" even though I only use one. ("water_volume" is used by other scripts and I didn't want to make multiple signals).
- Anyway, hope that helps. the best is to play around with a simple set and see what happens.
- Obviously, I learned all this yesterday so I might be wrong on some points. Please be kind on me and enlighten me in that case :)
- permalinkembedparent
- [–]Duke_meister 1 point 2 days ago
- Thanks, I added a bit on parameter passing, I think it's right.
- permalinkembedparent
- [–]Mylon 1 point 2 days ago
- The main purpose of signals is to have path-independent calls. If you want to call on_water_rise() you don't have to find each node that does this function and call it individually. Instead you just emit the signal from any node and every node that has listener will call that method.
- permalinkembedparent
- [–]Geaxle 1 point 2 days ago
- What if the method is specific to each node? I mean, in my case I want to update the UI, and some calculation in another one. That's not exactly the same thing.
- permalinkembedparent
- [–]Mylon 1 point 2 days ago
- Also perfect for signals.
- UiNode.on_water_rise() can update your UI and TerrainNode.on_water_rise() can run some calculations.
- The important part here is that the signal replaces the need to iterate over the entire node tree and do "if node.has_method("on_water_rise"): node.on_water_rise()"
- permalinkembedparent
- [–]Geaxle 1 point 2 days ago
- Ah yes, definitely, that's how I use it. Sorry if there was a misunderstanding. :)
- permalinkembedparent
Add Comment
Please, Sign In to add comment