123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- .. _doc_signals:
- Signals
- =======
- Introduction
- ------------
- Signals are Godot's version of the *observer* pattern. They allow a node to
- send out a message that other nodes can listen for and respond to. For example,
- rather than continuously checking a button to see if it's being pressed, the
- button can emit a signal when it's pressed.
- .. note:: You can read more about the observer pattern here: http://gameprogrammingpatterns.com/observer.html
- Signals are a way to *decouple* your game objects, which leads to better organized
- and more manageable code. Instead of forcing game objects to expect other objects
- to always be present, they can instead emit signals that any interested objects can
- subscribe to and respond.
- Below you can see some examples of how you can use signals in your own projects.
- Timer example
- -------------
- To see how signals work, let's try using a :ref:`Timer <class_Timer>` node. Create
- a new scene with a Node and two children: a Timer and a :ref:`Sprite <class_Sprite>`.
- You can use the Godot icon for the Sprite's texture, or any other image you
- like. Attach a script to the root node, but don't add any code to it yet.
- Your scene tree should look like this:
- .. image:: img/signals_node_setup.png
- In the Timer node's properties, check the "On" box next to *Autostart*. This will
- cause the timer to start automatically when you run the scene. You can leave the
- *Wait Time* at 1 second.
- Next to the "Inspector" tab is a tab labeled "Node". Click on this tab and you'll
- see all of the signals that the selected node can emit. In the case of the Timer
- node, the one we're concerned with is "timeout". This signal is emitted whenever
- the Timer reaches ``0``.
- .. image:: img/signals_node_tab_timer.png
- Click on the "timeout()" signal and click "Connect...". You'll see the following
- window, where you can define how you want to connect the signal:
- .. image:: img/signals_connect_dialog_timer.png
- On the left side, you'll see the nodes in your scene and can select the node that
- you want to "listen" for the signal. Note that the Timer node is red - this is
- *not* an error, but is a visual indication that it's the node that is emitting
- the signal. Select the root node.
- .. warning:: The target node *must* have a script attached or you'll receive
- an error message.
- On the bottom of the window is a field labeled "Method In Node". This is the name
- of the function in the target node's script that you want to use. By default,
- Godot will create this function using the naming convention ``_on_<node_name>_<signal_name>``
- but you can change it if you wish.
- Click "Connect" and you'll see that the function has been created in the script:
- .. tabs::
- .. code-tab:: gdscript GDScript
- extends Node
- func _on_Timer_timeout():
- pass # replace with function body
- .. code-tab:: csharp
- public class TimerExample : Node
- {
- private void _on_Timer_timeout()
- {
- // Replace with function body.
- }
- }
- Now we can replace the placeholder code with whatever code we want to run when
- the signal is received. Let's make the Sprite blink:
- .. tabs::
- .. code-tab:: gdscript GDScript
- extends Node
- func _on_Timer_timeout():
- $Sprite.visible = !$Sprite.visible
- .. code-tab:: csharp
- public class TimerExample : Node
- {
- public void _on_Timer_timeout()
- {
- var sprite = GetNode<Sprite>("Sprite");
- sprite.Visible = !sprite.Visible;
- }
- }
- Run the scene and you'll see the Sprite blinking on and off every second. You can
- change the Timer's *Wait Time* property to alter this.
- Connecting signals in code
- ~~~~~~~~~~~~~~~~~~~~~~~~~~
- You can also make the signal connection in code rather than with the editor. This
- is usually necessary when you're instancing nodes via code and so you can't use
- the editor to make the connection.
- First, disconnect the signal by selecting the connection in the Timer's "Node"
- tab and clicking disconnect.
- .. image:: img/signals_disconnect_timer.png
- To make the connection in code, we can use the ``connect`` function. We'll put it
- in ``_ready()`` so that the connection will be made on run. The syntax of the
- function is ``<source_node>.connect(<signal_name>, <target_node>, <target_function_name>)``.
- Here is the code for our Timer connection:
- .. tabs::
- .. code-tab:: gdscript GDScript
- extends Node
- func _ready():
- $Timer.connect("timeout", self, "_on_Timer_timeout")
- func _on_Timer_timeout():
- $Sprite.visible = !$Sprite.visible
- .. code-tab:: csharp
- public class TimerExample : Node
- {
- public override void _Ready()
- {
- GetNode("Timer").Connect("timeout", this, nameof(_on_Timer_timeout));
- }
- public void _on_Timer_timeout()
- {
- var sprite = GetNode<Sprite>("Sprite");
- sprite.Visible = !sprite.Visible;
- }
- }
- Custom signals
- --------------
- You can also declare your own custom signals in Godot:
- .. tabs::
- .. code-tab:: gdscript GDScript
- extends Node
- signal my_signal
- .. code-tab:: csharp
- public class Main : Node
- {
- [Signal]
- public delegate void MySignal();
- }
- Once declared, your custom signals will appear in the Inspector and can be connected
- in the same way as a node's built-in signals.
- To emit a signal via code, use the ``emit`` function:
- .. tabs::
- .. code-tab:: gdscript GDScript
- extends Node
- signal my_signal
- func _ready():
- emit_signal("my_signal")
- .. code-tab:: csharp
- public class Main : Node
- {
- [Signal]
- public delegate void MySignal();
- public override void _Ready()
- {
- EmitSignal(nameof(MySignal));
- }
- }
- Shooting example
- ----------------
- As another example of signal usage, let's consider a player character that can
- rotate and shoot towards the mouse. Every time the mouse button is clicked,
- we create an instance of the bullet at the player's location. See :ref:`doc_instancing`
- for details.
- However, if the bullets are added as children of the player, then they will
- remain "attached" to the player as it rotates:
- .. image:: img/signals_shoot1.gif
- Instead, we need the bullets to be independent of the player's movement - once
- fired, they should continue traveling in a straight line and the player can no
- longer affect them. Instead of being added to the scene tree as a child of the
- player, it makes more sense to add the bullet as a child of the "main" game
- scene, which may be the player's parent or even further up the tree.
- You could do this by adding the bullet directly:
- .. tabs::
- .. code-tab:: gdscript GDScript
- var bullet_instance = Bullet.instance()
- get_parent().add_child(bullet_instance)
- .. code-tab:: csharp
- Node bulletInstance = Bullet.Instance();
- GetParent().AddChild(bulletInstance);
- However, this will lead to a different problem. Now if you try and test your
- "Player" scene independently, it will crash on shooting, because there is no
- parent node to access. This makes it a lot harder to test your player code
- independently and also means that if you decide to change your main scene's
- node structure, the player's parent may no longer be the appropriate node to
- receive the bullets.
- The solution to this is to use a signal to "emit" the bullets from the player.
- The player then has no need to "know" what happens to the bullets after that -
- whatever node is connected to the signal can "receive" the bullets and take the
- appropriate action to spawn them.
- Here is the code for the player using signals to emit the bullet:
- .. tabs::
- .. code-tab:: gdscript GDScript
- extends Sprite
- signal shoot(bullet, direction, location)
- var Bullet = preload("res://Bullet.tscn")
- func _input(event):
- if event is InputEventMouseButton:
- if event.button_index == BUTTON_LEFT and event.pressed:
- emit_signal("shoot", Bullet, rotation, position)
- func _process(delta):
- look_at(get_global_mouse_position())
- .. code-tab:: csharp
- public class Player : Sprite
- {
- [Signal]
- delegate void Shoot(PackedScene bullet, Vector2 direction, Vector2 location);
- PackedScene _bullet = GD.Load<PackedScene>("res://Bullet.tscn");
- public override void _Input(InputEvent event)
- {
- if (input is InputEventMouseButton && Input.IsMouseButtonPressed((int)ButtonList.Left))
- {
- EmitSignal(nameof(Shoot), _bullet, Rotation, Position);
- }
- }
- public override _Process(float delta)
- {
- LookAt(GetGlobalMousePosition());
- }
- }
- In the main scene, we then connect the player's signal (it will appear in the
- "Node" tab).
- .. tabs::
- .. code-tab:: gdscript GDScript
- func _on_Player_shoot(Bullet, direction, location):
- var b = Bullet.instance()
- add_child(b)
- b.rotation = direction
- b.position = location
- b.velocity = b.velocity.rotated(direction)
- .. code-tab:: csharp
- public void _on_Player_Shoot(PackedScene bullet, Vector2 direction, Vector2 location)
- {
- var bulletInstance = (Bullet)bullet.Instance();
- AddChild(bulletInstance);
- bulletInstance.Rotation = direction;
- bulletInstance.Position = location;
- bulletInstance.Velocity = bulletInstance.Velocity.Rotated(direction);
- }
- Now the bullets will maintain their own movement independent of the player's
- rotation:
- .. image:: img/signals_shoot2.gif
- Conclusion
- ----------
- Many of Godot's built-in node types provide signals you can use to detect
- events. For example, an :ref:`Area2D <class_Area2D>` representing a coin emits
- a ``body_entered`` signal whenever the player's physics body enters its collision
- shape, allowing you to know when the player collected it.
- In the next section, :ref:`doc_your_first_game`, you'll build a complete game
- including several uses of signals to connect different game components.
|