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

Żeby zobaczyć, jak działają sygnały, użyjmy węzła Timer. Utwórz nową scenę z węzłem typu Node oraz jego dwoma węzłami-dziećmi: węzłem Timer oraz a Sprite. W doku sceny, zmień nazwę węzła z Node na 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ę.

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.

../../_images/signals_node_tab_timer.png

Kliknij na sygnał „timeout()” i wybierz „Connect…”. Zobaczysz wtedy okno, w którym będziesz mógł zdefiniować, w jaki sposób chcesz połączyć sygnał:

../../_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 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.

Ostrzeżenie

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

Na dole okna znajduje się pole „Metoda w węźle”. Jest to nazwa funkcji w skrypcie docelowego węzła, której chcesz użyć. Domyślnie Godot utworzy tę funkcję, korzystając z następującej konwencji : _on_<nazwa_wezla>_<nazwa_sygnalu> . Oczywiście, jeśli chcesz, możesz zmienić tę nazwę według uznania.

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

extends Node

func _on_Timer_timeout():
    pass # replace with function body
public class TimerExample : Node
{
    private 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 Node

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 : Node
{
    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 Node

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

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

Niestandardowe sygnały

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

extends Node

signal my_signal
public class Main : Node
{
    [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 Node

signal my_signal

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

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

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.