シグナル

はじめに

シグナルは、observerパターンのGodotバージョンです。これにより、ノードは、他のノードが聞いて応答できるメッセージを発信できます。例えば、ボタンが押されているかどうかを継続的に確認するのではなく、ボタンが押されたときに信号を発することができます。

注釈

オブザーバーパターンの詳細については、以下をご覧ください (英語): http://gameprogrammingpatterns.com/observer.html

シグナルは、ゲーム オブジェクトを分離する方法であり、コードの整理と管理が容易になります。常に複数のゲーム オブジェクト同士を強制的に会わせるのではなく、代わりにオブジェクトはシグナルを発信でき、関係するすべてのオブジェクトは予約および応答ができます。

以下に、独自のプロジェクトでシグナルを使用する方法の例をいくつか示します。

タイマーの例

シグナルがどのように機能するかを見るために、Timerノードを使用してみましょう。 Node2Dと2つの子:TimerとSpriteで新しいシーンを作成します。 Sceneドックで、Node2Dの名前をTimerExampleに変更します。

スプライトのテクスチャには、Godotアイコン、またはその他の好きな画像を使用できます。そのためには、スプライトのテクスチャ属性ドロップダウンメニューで 読込み を選択します。ルートノードにスクリプトをアタッチしますが、まだコードを追加しないでください。

シーンツリーは次のようになります:

../../_images/signals_node_setup.png

Timerノードのプロパティで、Autostartの横にある「オン」ボックスをクリックします。これにより、シーンの実行時にタイマーが自動的に開始されます。Wait Timeは1秒のままにしておきましょう。

「インスペクタ」タブの隣には、「ノード」というラベルの付いたタブがあります。 このタブをクリックすると、選択したノードが発信できるすべての信号が表示されます。 Timerノードの場合、関係するのは「timeout」です。 このシグナルは、タイマーが 0 に到達するたびに出力されます。

../../_images/signals_node_tab_timer.png

シグナルパネルの下にある「timeout()」シグナルをクリックし、「接続...」をクリックします。 次のウィンドウが表示され、信号の接続方法を定義できます:

../../_images/signals_connect_dialog_timer.png

左側でシーンのノードを見ることができます。信号として「監視」したいノードを選択できます。タイマーノードは青色で、ノードが信号を発していること意味します。ルートノードを選択してください。

警告

ターゲットノードにはスクリプトがアタッチされている必要があります。そうしないと、エラーメッセージが表示されます。

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.

ウィンドウの下部には、「メソッドの選択」というラベルのフィールドがあります。 これは、使用したいターゲットノードのスクリプト内の関数の名前です。 デフォルトでは、Godotは_on_<ノード名>_<シグナル名>という命名規則を使用してこの関数を作成しますが、必要に応じて変更できます。

「接続」をクリックすると、スクリプトで関数が作成されたことがわかります:

extends Node2D


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

これで、仮置きのコードを、シグナルの受信時に実行したいコードに置き換えることができます。 スプライトを点滅させましょう:

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

シーンを実行すると、スプライトが1秒ごとに点滅します。 これを変更するには、タイマーのWait Timeプロパティを変更します。

コード内でシグナルをを接続する

エディタではなくコードでシグナル接続を行うこともできます。 これは通常、コードを介してノードをインスタンス化するときに必要であり、エディタを使用して接続することはできません。

まず、タイマーの[ノード]タブで接続を選択し、[切断]をクリックして、シグナルを切断します。

../../_images/signals_disconnect_timer.png

コードで接続するために、connect関数を使用できます。接続が実行時に行われるように、その関数を_ready()に入れます。関数の構文は <source_node>.connect(<signal_name>, <target_node>, <target_function_name>) です。タイマー接続のコードは次のとおりです:

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

カスタムシグナル

Godotでは独自のカスタムシグナルを宣言することもできます:

extends Node2D


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

一度宣言すると、カスタムシグナルがインスペクタに表示され、ノードの組み込みシグナルと同じ方法で接続できます。

コードを介してシグナルを発信するには、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));
    }
}

シグナルは任意により複数の引数を付けて宣言できます。カッコの中に引数の名前を指定します:

extends Node


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

注釈

シグナル引数はエディタのノードドックに表示され、Godotはそれらを使用してコールバック関数を生成できます。しかしながら、シグナルを発信するときに、任意の数の引数を発行できます。正しい値を出力するのはあなた次第です。

値を渡すには、それらをemit_signal 関数の2番目の引数として追加します:

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

結論

Godotの組み込みノードタイプの多くは、イベントの検出に使用できる信号を提供します。 たとえば、コインの見た目をしたArea2Dは、プレイヤーの物理ボディが衝突形状(コリジョンシェイプ)に入るたびに body_entered シグナルを発し、プレイヤーがそれを収集したタイミングを知ることができます。

次のセクション最初のゲームでは、さまざまなゲームコンポーネントを接続するシグナルのいくつかの使用法を含む、完全なゲームを構築します。