c_sharp_signals.rst 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. .. _doc_c_sharp_signals:
  2. C# signals
  3. ==========
  4. For a detailed explanation of signals in general, see the :ref:`doc_signals` section in the step
  5. by step tutorial.
  6. While it is still possible to use signals through the ``Connect``/``Disconnect`` API, C# gives us
  7. a more idiomatic way to implement the :ref:`observer pattern<doc_key_concepts_signals>`.
  8. Signals as C# events
  9. --------------------
  10. To provide more type-safety, Godot signals are also all available through `events <https://learn.microsoft.com/en-us/dotnet/csharp/events-overview>`_.
  11. You can handle these events, as any other event, with the ``+=`` and ``-=`` operators.
  12. .. code-block:: csharp
  13. Timer myTimer = GetNode<Timer>("Timer");
  14. myTimer.Timeout += () => GD.Print("Timeout!");
  15. In addition, you can always access signal names associated with a node type through its nested
  16. ``SignalName`` class. This is useful when, for example, you want to await on a signal (see :ref:`doc_c_sharp_differences_await`).
  17. .. code-block:: csharp
  18. await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
  19. .. note::
  20. Godot will take care of disconnecting all the signals you connected through events when your
  21. nodes are freed. Meaning that: as you don't need to call ``Disconnect`` on all signals you used
  22. ``Connect`` on, you don't need to ``-=`` all the signals you used ``+=`` on.
  23. Custom signals as C# events
  24. ---------------------------
  25. To declare a custom event in your C# script, use the ``[Signal]`` attribute on a public delegate type.
  26. Note that the name of this delegate needs to end with ``EventHandler``.
  27. .. code-block:: csharp
  28. [Signal]
  29. public delegate void MySignalEventHandler();
  30. [Signal]
  31. public delegate void MySignalWithArgumentEventHandler(string myString);
  32. Once this is done, Godot will create the appropriate events automatically behind the scenes. You
  33. can then use said events as you'd do for any other Godot signal. Note that events are named using
  34. your delegate's name minus the final ``EventHandler`` part.
  35. .. code-block:: csharp
  36. public override void _Ready()
  37. {
  38. MySignal += () => GD.Print("Hello!");
  39. MySignalWithArgument += SayHelloTo;
  40. }
  41. private void SayHelloTo(string name)
  42. {
  43. GD.Print($"Hello {name}!");
  44. }
  45. .. warning::
  46. If you want to connect to these signals in the editor, you will need to (re)build the project
  47. to see them appear.
  48. You can click the **Build** button in the upper-right corner of the editor to do so.
  49. Signal emission
  50. ---------------
  51. To emit signals, use the ``EmitSignal`` method. Note that, as for signals defined by the engine,
  52. your custom signal names are listed under the nested ``SignalName`` class.
  53. .. code-block:: csharp
  54. public void MyMethodEmittingSignals()
  55. {
  56. EmitSignal(SignalName.MySignal);
  57. EmitSignal(SignalName.MySignalWithArgument, "World");
  58. }
  59. In contrast with other C# events, you cannot use ``Invoke`` to raise events tied to Godot signals.
  60. Signals support arguments of any :ref:`Variant-compatible <doc_c_sharp_variant>` type.
  61. Consequently, any ``Node`` or ``Reference`` will be compatible automatically, but custom data objects will need
  62. to inherit from ``GodotObject`` or one of its subclasses.
  63. .. code-block:: csharp
  64. using Godot;
  65. public partial class DataObject : GodotObject
  66. {
  67. public string MyFirstString { get; set; }
  68. public string MySecondString { get; set; }
  69. }
  70. Bound values
  71. ------------
  72. Sometimes you'll want to bind values to a signal when the connection is established, rather than
  73. (or in addition to) when the signal is emitted. To do so, you can use an anonymous function like in
  74. the following example.
  75. Here, the :ref:`Button.Pressed <class_BaseButton_signal_pressed>` signal do not take any argument. But we
  76. want to use the same ``ModifyValue`` for both the "plus" and "minus" buttons. So we bind the
  77. modifier value at the time we're connecting the signals.
  78. .. code-block:: csharp
  79. public int Value { get; private set; } = 1;
  80. public override void _Ready()
  81. {
  82. Button plusButton = GetNode<Button>("PlusButton");
  83. plusButton.Pressed += () => ModifyValue(1);
  84. Button minusButton = GetNode<Button>("MinusButton");
  85. minusButton.Pressed += () => ModifyValue(-1);
  86. }
  87. private void ModifyValue(int modifier)
  88. {
  89. Value += modifier;
  90. }
  91. Signal creation at runtime
  92. --------------------------
  93. Finally, you can create custom signals directly while your game is running. Use the ``AddUserSignal``
  94. method for that. Be aware that it should be executed before any use of said signals (either
  95. connecting to them or emitting them). Also, note that signals created this way won't be visible through the
  96. ``SignalName`` nested class.
  97. .. code-block:: csharp
  98. public override void _Ready()
  99. {
  100. AddUserSignal("MyCustomSignal");
  101. EmitSignal("MyCustomSignal");
  102. }