signals.rst 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. .. _doc_signals:
  2. Signals
  3. =======
  4. Introduction
  5. ------------
  6. Signals are Godot's version of the *observer* pattern. They allow a node to
  7. send out a message that other nodes can listen for and respond to. For example,
  8. rather than continuously checking a button to see if it's being pressed, the
  9. button can emit a signal when it's pressed.
  10. .. note:: You can read more about the observer pattern here: http://gameprogrammingpatterns.com/observer.html
  11. Signals are a way to *decouple* your game objects, which leads to better organized
  12. and more manageable code. Instead of forcing game objects to expect other objects
  13. to always be present, they can instead emit signals that any interested objects can
  14. subscribe to and respond.
  15. Below you can see some examples of how you can use signals in your own projects.
  16. Timer example
  17. -------------
  18. To see how signals work, let's try using a :ref:`Timer <class_Timer>` node. Create
  19. a new scene with a Node and two children: a Timer and a :ref:`Sprite <class_Sprite>`.
  20. You can use the Godot icon for the Sprite's texture, or any other image you
  21. like. Attach a script to the root node, but don't add any code to it yet.
  22. Your scene tree should look like this:
  23. .. image:: img/signals_node_setup.png
  24. In the Timer node's properties, check the "On" box next to *Autostart*. This will
  25. cause the timer to start automatically when you run the scene. You can leave the
  26. *Wait Time* at 1 second.
  27. Next to the "Inspector" tab is a tab labeled "Node". Click on this tab and you'll
  28. see all of the signals that the selected node can emit. In the case of the Timer
  29. node, the one we're concerned with is "timeout". This signal is emitted whenever
  30. the Timer reaches ``0``.
  31. .. image:: img/signals_node_tab_timer.png
  32. Click on the "timeout()" signal and click "Connect...". You'll see the following
  33. window, where you can define how you want to connect the signal:
  34. .. image:: img/signals_connect_dialog_timer.png
  35. On the left side, you'll see the nodes in your scene and can select the node that
  36. you want to "listen" for the signal. Note that the Timer node is red - this is
  37. *not* an error, but is a visual indication that it's the node that is emitting
  38. the signal. Select the root node.
  39. .. warning:: The target node *must* have a script attached or you'll receive
  40. an error message.
  41. On the bottom of the window is a field labeled "Method In Node". This is the name
  42. of the function in the target node's script that you want to use. By default,
  43. Godot will create this function using the naming convention ``_on_<node_name>_<signal_name>``
  44. but you can change it if you wish.
  45. Click "Connect" and you'll see that the function has been created in the script:
  46. .. tabs::
  47. .. code-tab:: gdscript GDScript
  48. extends Node
  49. func _on_Timer_timeout():
  50. pass # replace with function body
  51. .. code-tab:: csharp
  52. public class TimerExample : Node
  53. {
  54. private void _on_Timer_timeout()
  55. {
  56. // Replace with function body.
  57. }
  58. }
  59. Now we can replace the placeholder code with whatever code we want to run when
  60. the signal is received. Let's make the Sprite blink:
  61. .. tabs::
  62. .. code-tab:: gdscript GDScript
  63. extends Node
  64. func _on_Timer_timeout():
  65. $Sprite.visible = !$Sprite.visible
  66. .. code-tab:: csharp
  67. public class TimerExample : Node
  68. {
  69. public void _on_Timer_timeout()
  70. {
  71. var sprite = GetNode<Sprite>("Sprite");
  72. sprite.Visible = !sprite.Visible;
  73. }
  74. }
  75. Run the scene and you'll see the Sprite blinking on and off every second. You can
  76. change the Timer's *Wait Time* property to alter this.
  77. Connecting signals in code
  78. ~~~~~~~~~~~~~~~~~~~~~~~~~~
  79. You can also make the signal connection in code rather than with the editor. This
  80. is usually necessary when you're instancing nodes via code and so you can't use
  81. the editor to make the connection.
  82. First, disconnect the signal by selecting the connection in the Timer's "Node"
  83. tab and clicking disconnect.
  84. .. image:: img/signals_disconnect_timer.png
  85. To make the connection in code, we can use the ``connect`` function. We'll put it
  86. in ``_ready()`` so that the connection will be made on run. The syntax of the
  87. function is ``<source_node>.connect(<signal_name>, <target_node>, <target_function_name>)``.
  88. Here is the code for our Timer connection:
  89. .. tabs::
  90. .. code-tab:: gdscript GDScript
  91. extends Node
  92. func _ready():
  93. $Timer.connect("timeout", self, "_on_Timer_timeout")
  94. func _on_Timer_timeout():
  95. $Sprite.visible = !$Sprite.visible
  96. .. code-tab:: csharp
  97. public class TimerExample : Node
  98. {
  99. public override void _Ready()
  100. {
  101. GetNode("Timer").Connect("timeout", this, nameof(_on_Timer_timeout));
  102. }
  103. public void _on_Timer_timeout()
  104. {
  105. var sprite = GetNode<Sprite>("Sprite");
  106. sprite.Visible = !sprite.Visible;
  107. }
  108. }
  109. Custom signals
  110. --------------
  111. You can also declare your own custom signals in Godot:
  112. .. tabs::
  113. .. code-tab:: gdscript GDScript
  114. extends Node
  115. signal my_signal
  116. .. code-tab:: csharp
  117. public class Main : Node
  118. {
  119. [Signal]
  120. public delegate void MySignal();
  121. }
  122. Once declared, your custom signals will appear in the Inspector and can be connected
  123. in the same way as a node's built-in signals.
  124. To emit a signal via code, use the ``emit`` function:
  125. .. tabs::
  126. .. code-tab:: gdscript GDScript
  127. extends Node
  128. signal my_signal
  129. func _ready():
  130. emit_signal("my_signal")
  131. .. code-tab:: csharp
  132. public class Main : Node
  133. {
  134. [Signal]
  135. public delegate void MySignal();
  136. public override void _Ready()
  137. {
  138. EmitSignal(nameof(MySignal));
  139. }
  140. }
  141. Shooting example
  142. ----------------
  143. As another example of signal usage, let's consider a player character that can
  144. rotate and shoot towards the mouse. Every time the mouse button is clicked,
  145. we create an instance of the bullet at the player's location. See :ref:`doc_instancing`
  146. for details.
  147. However, if the bullets are added as children of the player, then they will
  148. remain "attached" to the player as it rotates:
  149. .. image:: img/signals_shoot1.gif
  150. Instead, we need the bullets to be independent of the player's movement - once
  151. fired, they should continue traveling in a straight line and the player can no
  152. longer affect them. Instead of being added to the scene tree as a child of the
  153. player, it makes more sense to add the bullet as a child of the "main" game
  154. scene, which may be the player's parent or even further up the tree.
  155. You could do this by adding the bullet directly:
  156. .. tabs::
  157. .. code-tab:: gdscript GDScript
  158. var bullet_instance = Bullet.instance()
  159. get_parent().add_child(bullet_instance)
  160. .. code-tab:: csharp
  161. Node bulletInstance = Bullet.Instance();
  162. GetParent().AddChild(bulletInstance);
  163. However, this will lead to a different problem. Now if you try and test your
  164. "Player" scene independently, it will crash on shooting, because there is no
  165. parent node to access. This makes it a lot harder to test your player code
  166. independently and also means that if you decide to change your main scene's
  167. node structure, the player's parent may no longer be the appropriate node to
  168. receive the bullets.
  169. The solution to this is to use a signal to "emit" the bullets from the player.
  170. The player then has no need to "know" what happens to the bullets after that -
  171. whatever node is connected to the signal can "receive" the bullets and take the
  172. appropriate action to spawn them.
  173. Here is the code for the player using signals to emit the bullet:
  174. .. tabs::
  175. .. code-tab:: gdscript GDScript
  176. extends Sprite
  177. signal shoot(bullet, direction, location)
  178. var Bullet = preload("res://Bullet.tscn")
  179. func _input(event):
  180. if event is InputEventMouseButton:
  181. if event.button_index == BUTTON_LEFT and event.pressed:
  182. emit_signal("shoot", Bullet, rotation, position)
  183. func _process(delta):
  184. look_at(get_global_mouse_position())
  185. .. code-tab:: csharp
  186. public class Player : Sprite
  187. {
  188. [Signal]
  189. delegate void Shoot(PackedScene bullet, Vector2 direction, Vector2 location);
  190. PackedScene _bullet = GD.Load<PackedScene>("res://Bullet.tscn");
  191. public override void _Input(InputEvent event)
  192. {
  193. if (input is InputEventMouseButton && Input.IsMouseButtonPressed((int)ButtonList.Left))
  194. {
  195. EmitSignal(nameof(Shoot), _bullet, Rotation, Position);
  196. }
  197. }
  198. public override _Process(float delta)
  199. {
  200. LookAt(GetGlobalMousePosition());
  201. }
  202. }
  203. In the main scene, we then connect the player's signal (it will appear in the
  204. "Node" tab).
  205. .. tabs::
  206. .. code-tab:: gdscript GDScript
  207. func _on_Player_shoot(Bullet, direction, location):
  208. var b = Bullet.instance()
  209. add_child(b)
  210. b.rotation = direction
  211. b.position = location
  212. b.velocity = b.velocity.rotated(direction)
  213. .. code-tab:: csharp
  214. public void _on_Player_Shoot(PackedScene bullet, Vector2 direction, Vector2 location)
  215. {
  216. var bulletInstance = (Bullet)bullet.Instance();
  217. AddChild(bulletInstance);
  218. bulletInstance.Rotation = direction;
  219. bulletInstance.Position = location;
  220. bulletInstance.Velocity = bulletInstance.Velocity.Rotated(direction);
  221. }
  222. Now the bullets will maintain their own movement independent of the player's
  223. rotation:
  224. .. image:: img/signals_shoot2.gif
  225. Conclusion
  226. ----------
  227. Many of Godot's built-in node types provide signals you can use to detect
  228. events. For example, an :ref:`Area2D <class_Area2D>` representing a coin emits
  229. a ``body_entered`` signal whenever the player's physics body enters its collision
  230. shape, allowing you to know when the player collected it.
  231. In the next section, :ref:`doc_your_first_game`, you'll build a complete game
  232. including several uses of signals to connect different game components.