Signaalit

Johdanto

Signaalit ovat Godotin versio tarkkailija-mallista. Ne antavat solmun lähettää viestejä, joita muut solmut voivat kuunnella ja joihin ne voivat vastata. Esimerkiksi sen sijaan, että tarkistaisi jatkuvasti onko painiketta painettu, painike voi lähettää signaalin kun sitä on painettu.

Muista

You can read more about the observer pattern here: https://gameprogrammingpatterns.com/observer.html

Signaalien avulla pelin objektit voidaan erottaa toisistaan, mikä johtaa paremmin järjestettyyn ja hallittavampaan koodiin. Sen sijaan, että objektien on pakko olettaa, että muut objektit ovat aina läsnä, ne voivat lähettää signaaleja, joita kaikki kiinnostuneet objektit voivat kirjautua kuuntelemaan ja vastaamaan.

Alla muutamia esimerkkejä siitä, miten voit käyttää signaaleja omissa projekteissasi.

Ajastin-esimerkki

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.

Spriten tekstuurille voit käyttää Godotin ikonia, tai mitä tahansa muuta kuvatiedostoa. Tee niin valitsemalla Load spriten tekstuuri attribuutin pudotusvalikosta. Lisää skripti root solmuun, mutta älä lisää siihen vielä mitään koodia.

Skenepuusi pitäisi näyttää tältä:

../../_images/signals_node_setup.png

Avaa Timer-solmun ominaisuudet ja napsauta Autostart-tekstin viereistä valintaruutua. Tämä käynnistää ajastimen automaattisesti, kun skene ajetaan. Wait Time-ominaisuus voi pysyä 1 sekunnissa.

Napsauta ”Inspector”-välilehden vieressä olevaa välilehteä ”Node”. Tältä välilehdeltä löytyvät kaikki signaalit, joita valittu solmu voi lähettää. Timer-solmun tapauksessa meitä kiinnostaa ”timeout”. Tämä signaali lähetetään, kun ajastin osoittaa 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.

Varoitus

Kohdesolmuun täytyy olla liitettynä skripti, muutoin saat virheilmoituksen.

If you toggle the Advanced menu, you'll see on the right side that 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.

Napsauta "Connect"-painiketta ja voit nähdä, että skriptiin on luotu funktio:

extends Node2D


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

Nyt oletuskoodi voidaan korvata millä tahansa koodilla, jonka haluamme signaalia vastaanottaessa suorittaa. Laitetaan kuvake välkkymään:

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;
    }
}

Aja skene ja näet, että kuvake välkkyy sekunnin välein. Ajoitusta voi muuttaa Timer-solmun Wait Time-ominaisuudella.

Signaalien yhdistäminen koodin kautta

Signaaleja voi yhdistää koodilla editorin sijaan. Tämä on usein tarpeen, kun solmuille luodaan ilmentymiä koodin kautta, eikä yhteyksiä voida luoda editorin avulla.

Irrota ensin signaali valitsemalla yhteys Timer-solmun "Node"-välilehdessä ja napsauttamalla "Disconnect"-painiketta.

../../_images/signals_disconnect_timer.png

Yhteyden muodostaminen koodilla onnistuu connect-funktion avulla. Laitetaan se _ready()-funktioon, jotta yhteys muodostetaan ajon aikana. Funktion syntaksi on muodossa <lähdesolmu>.connect(<signaalin_nimi>, <kohdesolmu>, <kohdefunktion_nimi>). Timer-solmun yhteyden koodi näyttää tältä:

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;
    }
}

Mukautetut signaalit

Voit määritellä myös omia mukautettuja signaaleja Godotissa:

extends Node2D


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

Kun mukautetut signaalit on määritelty, ilmestyvät ne myös Inspector-välilehteen ja niitä voidaan yhdistää solmun sisäänrakennettujen signaalien tavoin.

Signaalin lähettämiseksi koodin kautta, käytä emit-funktiota:

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(bool value, int other_value);
}

Muista

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(bool value, int other_value);

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

Lopputulos

Monista Godotin valmiista solmutyypeistä löytyy signaaleja tapahtumien havaitsemiseen. Esimerkiksi kolikkoa ilmentävä Area2D-solmu lähettää ``body_entered`-signaalin kun pelihahmon fysiikkakappale siirtyy sen törmäysmuodon sisään. Tämän avulla saadaan selville milloin pelaaja on kerännyt kolikon.

Seuraavassa osiossa, Ensimmäinen pelisi, rakennamme kokonaisen pelin, jossa signaaleja käytetään monin eri tavoin yhdistämään pelin eri komponentteja.