signals.rst 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. .. Intention: give the user a first taste of signals. We should write more
  2. documentation in the scripting/ section.
  3. .. Note: GDScript snippets use one line return instead of two because they're
  4. really short.
  5. .. meta::
  6. :keywords: Signal
  7. .. _doc_signals:
  8. Using signals
  9. =============
  10. In this lesson, we will look at signals. They are messages that nodes emit when
  11. something specific happens to them, like a button being pressed. Other nodes can
  12. connect to that signal and call a function when the event occurs.
  13. Signals are a delegation mechanism built into Godot that allows one game object to
  14. react to a change in another without them referencing one another. Using signals
  15. limits `coupling
  16. <https://en.wikipedia.org/wiki/Coupling_(computer_programming)>`_ and keeps your
  17. code flexible.
  18. For example, you might have a life bar on the screen that represents the
  19. player's health. When the player takes damage or uses a healing potion, you want
  20. the bar to reflect the change. To do so, in Godot, you would use signals.
  21. Like methods (:ref:`class_callable`), signals are a first-class type since Godot
  22. 4.0. This means you can pass them around as method arguments directly without
  23. having to pass them as strings, which allows for better autocompletion and is
  24. less error-prone. See the :ref:`class_signal` class reference for a list of
  25. what you can do with the Signal type directly.
  26. .. seealso::
  27. As mentioned in the introduction, signals are Godot's version of the
  28. observer pattern. You can learn more about it in
  29. `Game Programming Patterns <https://gameprogrammingpatterns.com/observer.html>`__.
  30. We will now use a signal to make our Godot icon from the previous lesson
  31. (:ref:`doc_scripting_player_input`) move and stop by pressing a button.
  32. .. note:: For this project, we will be following the Godot naming conventions.
  33. - **GDScript**: Classes (nodes) use PascalCase, variables and
  34. functions use snake_case, and constants use ALL_CAPS (See
  35. :ref:`doc_gdscript_styleguide`).
  36. - **C#**: Classes, export variables and methods use PascalCase,
  37. private fields use _camelCase, local variables and parameters use
  38. camelCase (See :ref:`doc_c_sharp_styleguide`). Be careful to type
  39. the method names precisely when connecting signals.
  40. Scene setup
  41. -----------
  42. To add a button to our game, we will create a new scene which will include
  43. both a :ref:`Button <class_button>` and the ``sprite_2d.tscn`` scene we created in
  44. the :ref:`doc_scripting_first_script` lesson.
  45. Create a new scene by going to the menu :menu:`Scene > New Scene`.
  46. .. image:: img/signals_01_new_scene.webp
  47. In the Scene dock, click the :button:`2D Scene`` button. This will add
  48. a :ref:`Node2D <class_Node2D>` as our root.
  49. .. image:: img/signals_02_2d_scene.webp
  50. In the FileSystem dock, click and drag the ``sprite_2d.tscn`` file you saved
  51. previously onto the Node2D to instantiate it.
  52. .. image:: img/signals_03_dragging_scene.png
  53. We want to add another node as a sibling of the Sprite2D. To do so, right-click
  54. on Node2D and select :button:`Add Child Node`.
  55. .. image:: img/signals_04_add_child_node.webp
  56. Search for the :ref:`Button <class_button>` node and add it.
  57. .. image:: img/signals_05_add_button.webp
  58. The node is small by default. Click and drag on the bottom-right handle of the
  59. Button in the viewport to resize it.
  60. .. image:: img/signals_06_drag_button.png
  61. If you don't see the handles, ensure the select tool is active in the toolbar.
  62. .. image:: img/signals_07_select_tool.webp
  63. Click and drag on the button itself to move it closer to the sprite.
  64. You can also write a label on the Button by editing its :inspector:`Text` property
  65. in the :ui:`Inspector`. Enter ``Toggle motion``.
  66. .. image:: img/signals_08_toggle_motion_text.webp
  67. Your scene tree and viewport should look like this.
  68. .. image:: img/signals_09_scene_setup.png
  69. Save your newly created scene as ``node_2d.tscn``, if you haven't already.
  70. You can then run it with :kbd:`F6` (:kbd:`Cmd + R` on macOS).
  71. At the moment, the button will be visible, but nothing will happen if you
  72. press it.
  73. Connecting a signal in the editor
  74. ---------------------------------
  75. Here, we want to connect the Button's "pressed" signal to our Sprite2D, and we
  76. want to call a new function that will toggle its motion on and off. We need to
  77. have a script attached to the Sprite2D node, which we do from the previous
  78. lesson.
  79. You can connect signals in the :ui:`Node` dock. Select the Button node and, on the
  80. right side of the editor, click on the tab named :ui:`Node` next to the
  81. :ui:`Inspector`.
  82. .. image:: img/signals_10_node_dock.webp
  83. The dock displays a list of signals available on the selected node.
  84. .. image:: img/signals_11_pressed_signals.webp
  85. Double-click the "pressed" signal to open the node connection window.
  86. .. image:: img/signals_12_node_connection.webp
  87. There, you can connect the signal to the Sprite2D node. The node needs a
  88. receiver method, a function that Godot will call when the Button emits the
  89. signal. The editor generates one for you. By convention, we name these callback
  90. methods "_on_node_name_signal_name". Here, it'll be "_on_button_pressed".
  91. .. note::
  92. When connecting signals via the editor's Node dock, you can use two
  93. modes. The simple one only allows you to connect to nodes that have a
  94. script attached to them and creates a new callback function on them.
  95. .. image:: img/signals_advanced_connection_window.png
  96. The advanced view lets you connect to any node and any built-in
  97. function, add arguments to the callback, and set options. You can
  98. toggle the mode in the window's bottom-right by clicking the :button:`Advanced`
  99. button.
  100. .. note::
  101. If you are using an external editor (such as VS Code), this
  102. automatic code generation might not work. In this case, you need to connect
  103. the signal via code as explained in the next section.
  104. Click the :button:`Connect` button to complete the signal connection and jump to the
  105. :ui:`Script` workspace. You should see the new method with a connection icon in the
  106. left margin.
  107. .. image:: img/signals_13_signals_connection_icon.webp
  108. If you click the icon, a window pops up and displays information about the
  109. connection. This feature is only available when connecting nodes in the editor.
  110. .. image:: img/signals_14_signals_connection_info.webp
  111. Let's replace the line with the ``pass`` keyword with code that'll toggle the
  112. node's motion.
  113. Our Sprite2D moves thanks to code in the ``_process()`` function. Godot provides
  114. a method to toggle processing on and off: :ref:`Node.set_process()
  115. <class_Node_method_set_process>`. Another method of the Node class,
  116. ``is_processing()``, returns ``true`` if idle processing is active. We can use
  117. the ``not`` keyword to invert the value.
  118. .. tabs::
  119. .. code-tab:: gdscript GDScript
  120. func _on_button_pressed():
  121. set_process(not is_processing())
  122. .. code-tab:: csharp C#
  123. // We also specified this function name in PascalCase in the editor's connection window.
  124. private void OnButtonPressed()
  125. {
  126. SetProcess(!IsProcessing());
  127. }
  128. This function will toggle processing and, in turn, the icon's motion on and off
  129. upon pressing the button.
  130. Before trying the game, we need to simplify our ``_process()`` function to move
  131. the node automatically and not wait for user input. Replace it with the
  132. following code, which we saw two lessons ago:
  133. .. tabs::
  134. .. code-tab:: gdscript GDScript
  135. func _process(delta):
  136. rotation += angular_speed * delta
  137. var velocity = Vector2.UP.rotated(rotation) * speed
  138. position += velocity * delta
  139. .. code-tab:: csharp C#
  140. public override void _Process(double delta)
  141. {
  142. Rotation += _angularSpeed * (float)delta;
  143. var velocity = Vector2.Up.Rotated(Rotation) * _speed;
  144. Position += velocity * (float)delta;
  145. }
  146. Your complete ``sprite_2d.gd`` code should look like the following.
  147. .. tabs::
  148. .. code-tab:: gdscript GDScript
  149. extends Sprite2D
  150. var speed = 400
  151. var angular_speed = PI
  152. func _process(delta):
  153. rotation += angular_speed * delta
  154. var velocity = Vector2.UP.rotated(rotation) * speed
  155. position += velocity * delta
  156. func _on_button_pressed():
  157. set_process(not is_processing())
  158. .. code-tab:: csharp C#
  159. using Godot;
  160. public partial class MySprite2D : Sprite2D
  161. {
  162. private float _speed = 400;
  163. private float _angularSpeed = Mathf.Pi;
  164. public override void _Process(double delta)
  165. {
  166. Rotation += _angularSpeed * (float)delta;
  167. var velocity = Vector2.Up.Rotated(Rotation) * _speed;
  168. Position += velocity * (float)delta;
  169. }
  170. // We also specified this function name in PascalCase in the editor's connection window.
  171. private void OnButtonPressed()
  172. {
  173. SetProcess(!IsProcessing());
  174. }
  175. }
  176. Run the current scene by pressing :kbd:`F6` (:kbd:`Cmd + R` on macOS),
  177. and click the button to see the sprite start and stop.
  178. Connecting a signal via code
  179. ----------------------------
  180. You can connect signals via code instead of using the editor. This is necessary
  181. when you create nodes or instantiate scenes inside of a script.
  182. Let's use a different node here. Godot has a :ref:`Timer <class_Timer>` node
  183. that's useful to implement skill cooldown times, weapon reloading, and more.
  184. Head back to the 2D workspace. You can either click the "2D" text at the top of
  185. the window or press :kbd:`Ctrl + F1` (:kbd:`Ctrl + Cmd + 1` on macOS).
  186. In the Scene dock, right-click on the Sprite2D node and add a new child node.
  187. Search for Timer and add the corresponding node. Your scene should now look like
  188. this.
  189. .. image:: img/signals_15_scene_tree.webp
  190. With the Timer node selected, go to the :ui:`Inspector` and enable the :inspector:`Autostart`
  191. property.
  192. .. image:: img/signals_18_timer_autostart.webp
  193. Click the script icon next to Sprite2D to jump back to the scripting workspace.
  194. .. image:: img/signals_16_click_script.webp
  195. We need to do two operations to connect the nodes via code:
  196. 1. Get a reference to the Timer from the Sprite2D.
  197. 2. Call the ``connect()`` method on the Timer's "timeout" signal.
  198. .. note:: To connect to a signal via code, you need to call the ``connect()``
  199. method of the signal you want to listen to. In this case, we want to
  200. listen to the Timer's "timeout" signal.
  201. We want to connect the signal when the scene is instantiated, and we can do that
  202. using the :ref:`Node._ready() <class_Node_private_method__ready>` built-in function,
  203. which is called automatically by the engine when a node is fully instantiated.
  204. To get a reference to a node relative to the current one, we use the method
  205. :ref:`Node.get_node() <class_Node_method_get_node>`. We can store the reference
  206. in a variable.
  207. .. tabs::
  208. .. code-tab:: gdscript GDScript
  209. func _ready():
  210. var timer = get_node("Timer")
  211. .. code-tab:: csharp C#
  212. public override void _Ready()
  213. {
  214. var timer = GetNode<Timer>("Timer");
  215. }
  216. The function ``get_node()`` looks at the Sprite2D's children and gets nodes by
  217. their name. For example, if you renamed the Timer node to "BlinkingTimer" in the
  218. editor, you would have to change the call to ``get_node("BlinkingTimer")``.
  219. .. add seealso to a page that explains node features.
  220. We can now connect the Timer to the Sprite2D in the ``_ready()`` function.
  221. .. tabs::
  222. .. code-tab:: gdscript GDScript
  223. func _ready():
  224. var timer = get_node("Timer")
  225. timer.timeout.connect(_on_timer_timeout)
  226. .. code-tab:: csharp C#
  227. public override void _Ready()
  228. {
  229. var timer = GetNode<Timer>("Timer");
  230. timer.Timeout += OnTimerTimeout;
  231. }
  232. The line reads like so: we connect the Timer's "timeout" signal to the node to
  233. which the script is attached. When the Timer emits ``timeout``, we want to call
  234. the function ``_on_timer_timeout()``, that we need to define. Let's add it at the
  235. bottom of our script and use it to toggle our sprite's visibility.
  236. .. note:: By convention, we name these callback methods in GDScript as
  237. "_on_node_name_signal_name" and in C# as "OnNodeNameSignalName".
  238. Here, it'll be "_on_timer_timeout" for GDScript and OnTimerTimeout() for C#.
  239. .. tabs::
  240. .. code-tab:: gdscript GDScript
  241. func _on_timer_timeout():
  242. visible = not visible
  243. .. code-tab:: csharp C#
  244. private void OnTimerTimeout()
  245. {
  246. Visible = !Visible;
  247. }
  248. The ``visible`` property is a boolean that controls the visibility of our node.
  249. The line ``visible = not visible`` toggles the value. If ``visible`` is
  250. ``true``, it becomes ``false``, and vice-versa.
  251. If you run the Node2D scene now, you will see that the sprite blinks on and off, at one
  252. second intervals.
  253. Complete script
  254. ---------------
  255. That's it for our little moving and blinking Godot icon demo!
  256. Here is the complete ``sprite_2d.gd`` file for reference.
  257. .. tabs::
  258. .. code-tab:: gdscript GDScript
  259. extends Sprite2D
  260. var speed = 400
  261. var angular_speed = PI
  262. func _ready():
  263. var timer = get_node("Timer")
  264. timer.timeout.connect(_on_timer_timeout)
  265. func _process(delta):
  266. rotation += angular_speed * delta
  267. var velocity = Vector2.UP.rotated(rotation) * speed
  268. position += velocity * delta
  269. func _on_button_pressed():
  270. set_process(not is_processing())
  271. func _on_timer_timeout():
  272. visible = not visible
  273. .. code-tab:: csharp C#
  274. using Godot;
  275. public partial class MySprite2D : Sprite2D
  276. {
  277. private float _speed = 400;
  278. private float _angularSpeed = Mathf.Pi;
  279. public override void _Ready()
  280. {
  281. var timer = GetNode<Timer>("Timer");
  282. timer.Timeout += OnTimerTimeout;
  283. }
  284. public override void _Process(double delta)
  285. {
  286. Rotation += _angularSpeed * (float)delta;
  287. var velocity = Vector2.Up.Rotated(Rotation) * _speed;
  288. Position += velocity * (float)delta;
  289. }
  290. // We also specified this function name in PascalCase in the editor's connection window.
  291. private void OnButtonPressed()
  292. {
  293. SetProcess(!IsProcessing());
  294. }
  295. private void OnTimerTimeout()
  296. {
  297. Visible = !Visible;
  298. }
  299. }
  300. Custom signals
  301. --------------
  302. .. note:: This section is a reference on how to define and use your own signals,
  303. and does not build upon the project created in previous lessons.
  304. You can define custom signals in a script. Say, for example, that you want to
  305. show a game over screen when the player's health reaches zero. To do so, you
  306. could define a signal named "died" or "health_depleted" when their health
  307. reaches 0.
  308. .. tabs::
  309. .. code-tab:: gdscript GDScript
  310. extends Node2D
  311. signal health_depleted
  312. var health = 10
  313. .. code-tab:: csharp C#
  314. using Godot;
  315. public partial class MyNode2D : Node2D
  316. {
  317. [Signal]
  318. public delegate void HealthDepletedEventHandler();
  319. private int _health = 10;
  320. }
  321. .. note:: As signals represent events that just occurred, we generally use an
  322. action verb in the past tense in their names.
  323. Your signals work the same way as built-in ones: they appear in the :ui:`Node` tab and
  324. you can connect to them like any other.
  325. .. image:: img/signals_17_custom_signal.webp
  326. To emit a signal in your scripts, call ``emit()`` on the signal.
  327. .. tabs::
  328. .. code-tab:: gdscript GDScript
  329. func take_damage(amount):
  330. health -= amount
  331. if health <= 0:
  332. health_depleted.emit()
  333. .. code-tab:: csharp C#
  334. public void TakeDamage(int amount)
  335. {
  336. _health -= amount;
  337. if (_health <= 0)
  338. {
  339. EmitSignal(SignalName.HealthDepleted);
  340. }
  341. }
  342. A signal can optionally declare one or more arguments. Specify the argument
  343. names between parentheses:
  344. .. tabs::
  345. .. code-tab:: gdscript GDScript
  346. extends Node2D
  347. signal health_changed(old_value, new_value)
  348. var health = 10
  349. .. code-tab:: csharp C#
  350. using Godot;
  351. public partial class MyNode : Node
  352. {
  353. [Signal]
  354. public delegate void HealthChangedEventHandler(int oldValue, int newValue);
  355. private int _health = 10;
  356. }
  357. .. note::
  358. The signal arguments show up in the editor's node dock, and Godot can use
  359. them to generate callback functions for you. However, you can still emit any
  360. number of arguments when you emit signals. So it's up to you to emit the
  361. correct values.
  362. To emit values along with the signal, add them as extra arguments to the
  363. ``emit()`` function:
  364. .. tabs::
  365. .. code-tab:: gdscript GDScript
  366. func take_damage(amount):
  367. var old_health = health
  368. health -= amount
  369. health_changed.emit(old_health, health)
  370. .. code-tab:: csharp C#
  371. public void TakeDamage(int amount)
  372. {
  373. int oldHealth = _health;
  374. _health -= amount;
  375. EmitSignal(SignalName.HealthChanged, oldHealth, _health);
  376. }
  377. Summary
  378. -------
  379. Any node in Godot emits signals when something specific happens to them, like a
  380. button being pressed. Other nodes can connect to individual signals and react to
  381. selected events.
  382. Signals have many uses. With them, you can react to a node entering or exiting
  383. the game world, to a collision, to a character entering or leaving an area, to
  384. an element of the interface changing size, and much more.
  385. For example, an :ref:`Area2D <class_Area2D>` representing a coin emits a
  386. ``body_entered`` signal whenever the player's physics body enters its collision
  387. shape, allowing you to know when the player collected it.
  388. In the next section, :ref:`doc_your_first_2d_game`, you'll create a complete 2D
  389. game and put everything you learned so far into practice.