Sygnały

Wprowadzenie

Sygnały w Godot są odpowednikiem wzorca obserwatora. Pozwalają węzłowi na wysyłanie wiadomości, na które inne węzły mogą nasłuchiwać i reagować. Przykładowo, zamiast bez przerwy sprawdzać, czy przycisk jest w momencie przyciskania, to przycisk może wyemitować sygnał, kiedy zostanie wciśnięty.

Informacja

O wzorcu projektowym obserwator, możesz przeczytać więcej tutaj: http://gameprogrammingpatterns.com/observer.html

Sygnały są sposobem na rozdzielenie Twoich obiektów, co powoduje, że pisany kod jest lepiej zorganizowany i łatwiej nim zarządzać. Zamiast wymuszać na obiektach, żeby zawsze spodziewały się określonych obiektów, mogą zamiast tego emitować sygnały, na które zareagują wszystkie zainteresowane obiekty.

Poniżej możesz zobaczyć przykłady, jak używać sygnałów w swoich projektach.

Przykład dla węzła Timer

To see how signals work, let’s try using a Timer node. Create a new scene with a Node2D and two children: a Timer and a Sprite. In the Scene dock, rename Node2D to TimerExample.

Dla tekstury węzła Sprite, możesz użyć ikony Godota lub jakiegokolwiek innego obrazu. Żeby to zrobić, naciśnij przycisk Załaduj w menu rozwijalnym atrybutu Tekstura, należącym do węzła Sprite. Dodaj skrypt do głównego węzła, ale nie dodawaj do niego jeszcze żadnego kodu.

Drzewo sceny powinno tak wyglądać:

../../_images/signals_node_setup.png

We właściwościach węzła Timer, zaznacz pole „Tak”, znajdujące się obok etykiety „Autostart”. To spowoduje, że zegar wystartuje automatycznie, kiedy uruchomisz scenę. Możesz zostawić Czas oczekiwania na 1 sekundę.

Obok zakładki „Inspektor” znajduje się zakładka z etykietą „Węzeł”. Kliknij na tę zakładkę, żeby zobaczyć wszystkie sygnały, jakie ten węzeł może wyemitować. w przypadku węzła typu Timer, zależy nam na sygnale „timeout”. Ten sygnał jest emitowany za każdym razem, kiedy czas Timera osiąga 0.

../../_images/signals_node_tab_timer.png

Click on the „timeout()” signal and click „Connect…” at the bottom of the signals panel. You’ll see the following window, where you can define how you want to connect the signal:

../../_images/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 blue, this is a visual indication that it’s the node that is emitting the signal. Select the root node.

Ostrzeżenie

Docelowy węzeł musi mieć skrypt, albo otrzymasz informację o błędzie.

On the right side, you can bind an arbitrary number of arguments of (possibly) different types. This can be useful when you have more than one signal connected to the same method, as each signal propagation will result in different values for those extra call arguments.

On the bottom of the window is a field labeled „Receiver Method”. 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.

Wybierz „Connect” i zobaczysz, że funkcja została utworzona w skrypcie:

extends Node2D

func _on_Timer_timeout():
    pass # replace with function body
public class TimerExample : Node2D
{
    public void _on_Timer_timeout()
    {
        // Replace with function body.
    }
}

Teraz możemy zastąpić tymczasowy kod jakimkolwiek innym, który ma być uruchomiony w momencie odebrania sygnału. Spowodujmy, żeby węzeł Sprite zaczął migać:

extends Node2D

func _on_Timer_timeout():
    # Note: the `$` operator is a shorthand for `get_node()`,
    # so `$Sprite` is equivalent to `get_node("Sprite")`.
    $Sprite.visible = !$Sprite.visible
public class TimerExample : Node2D
{
    public void _on_Timer_timeout()
    {
        var sprite = GetNode<Sprite>("Sprite");
        sprite.Visible = !sprite.Visible;
    }
}

Odtwórz scenę i zauważ, że węzeł Sprite miga z każdą sekundą. Możesz kontrolować czas migotania przy użyciu właściwości Czas oczekiwania, należącej do węzła Timer.

Podłączanie sygnałów w kodzie

Możesz także połączyć sygnał z poziomu kodu zamiast w edytorze. To jest zazwyczaj przydatne podczas tworzenia węzłów w kodzie, ponieważ nie można ustanowić połączenia dla tego węzła w edytorze.

Najpierw odłącz sygnał wybierając połączenie z zakładki „Węzeł”, należącej do węzła Timer i kliknij odłącz.

../../_images/signals_disconnect_timer.png

Aby dodać połączenie z poziomu kodu, może skorzystać z funkcji connect. Wstawimy tę funkcję do metody _ready() po to, żeby połączenie zostało nawiązane zaraz o po uruchomieniu. Składnia tej funkcji wygląda tak następująco: <wezel_zrodlowy>.connect(<nazwa_sygnalu>, <docelowy_wezel>, <nazwa_funkcji_docelowej>). Poniżej znajduje się kod dla połączenia węzła Timer:

extends Node2D

func _ready():
    $Timer.connect("timeout", self, "_on_Timer_timeout")

func _on_Timer_timeout():
    $Sprite.visible = !$Sprite.visible
public class TimerExample : Node2D
{
    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;
    }
}

Niestandardowe sygnały

W Godot możesz także zadeklarować własne rodzaje sygnałów:

extends Node2D

signal my_signal
public class Main : Node2D
{
    [Signal]
    public delegate void MySignal();
}

Raz zadeklarowane, twoje niestandardowe sygnały pojawią się w Inspektorze i będą mogły być podłączone w taki sam sposób jak sygnały wbudowane w węzeł.

Aby wyemitować sygnał poprzez kod, użyj funkcji emit_signal:

extends Node2D

signal my_signal

func _ready():
    emit_signal("my_signal")
public class Main : Node2D
{
    [Signal]
    public delegate void MySignal();

    public override void _Ready()
    {
        EmitSignal(nameof(MySignal));
    }
}

A signal can also optionally declare one or more arguments. Specify the argument names between parentheses:

extends Node

signal my_signal(value, other_value)
public class Main : Node
{
    [Signal]
    public delegate void MySignal();
}

Informacja

The signal arguments show up in the editor’s node dock, and Godot can use them to generate callback functions for you. However, you can still emit any number of arguments when you emit signals. So it’s up to you to emit the correct values.

To pass values, add them as the second argument to the emit_signal function:

extends Node

signal my_signal(value, other_value)

func _ready():
    emit_signal("my_signal", true, 42)
public class Main : Node
{
    [Signal]
    public delegate void MySignal();

    public override void _Ready()
    {
        EmitSignal(nameof(MySignal), true, 42);
    }
}

Wniosek

Godot ma wiele wbudowanych typów węzłów, które dostarczają sygnałów aby wykrywać wydarzenia. Na przykład, Area2D reprezentująca monetę emituje body_entered sygnał kiedy fizyczne ciało gracza wchodzi w jej kształt kolizyjny, co daję tobie znać kiedy gracz ją zebrał.

W następnej sekcji, Twoja pierwsza gra zbudujesz kompletną grę z wykorzystaniem sygnałów, aby połączyć różne elementy gry.