Godotの通知

Godotのすべてのオブジェクトは _notification メソッドを実装します。その目的は、オブジェクトがそれに関連する可能性のあるさまざまなエンジンレベルのコールバックに応答できるようにすることです。たとえば、エンジンが CanvasItem に「描画(draw)」するように指示すると、_ notification(NOTIFICATION_DRAW) が呼び出されます。

drawなどのこれらの通知の一部は、スクリプトでオーバーライドするのに役立ちます。Godotは、Godotがそれらの多くを専用関数で公開するほどです:

  • _ready() : NOTIFICATION_READY
  • _enter_tree() : NOTIFICATION_ENTER_TREE
  • _exit_tree() : NOTIFICATION_EXIT_TREE
  • _process(delta) : NOTIFICATION_PROCESS
  • _physics_process(delta) : NOTIFICATION_PHYSICS_PROCESS
  • _input() : NOTIFICATION_INPUT
  • _unhandled_input() : NOTIFICATION_UNHANDLED_INPUT
  • _draw() : NOTIFICATION_DRAW

ユーザーが気づかないかもしれないのは、ノードではないタイプの通知が存在するということです:

  • Object::NOTIFICATION_POSTINITIALIZE: オブジェクトの初期化中にトリガーされるコールバック。スクリプトからアクセスできません。
  • Object::NOTIFICATION_PREDELETE: エンジンがオブジェクトを削除、つまり「デストラクタ」の前にトリガーするコールバック。
  • MainLoop::NOTIFICATION_WM_MOUSE_ENTER: ゲームコンテンツを表示するオペレーティングシステムでマウスがウィンドウに入ったときにトリガーするコールバック。

また、ノードに存在するコールバックの多くには専用のメソッドはありませんが、それでも非常に便利です。

  • Node::NOTIFICATION_PARENTED: 子ノードを別のノードに追加するたびにトリガーするコールバック。
  • Node::NOTIFICATION_UNPARENTED: 別のノードから子ノードを削除するたびにトリガーするコールバック。
  • Popup::NOTIFICATION_POST_POPUP: ポップアップ ノードが popup* メソッドを完了した後にトリガーするコールバック。出現するにトリガーされる `` about_to_show``シグナルとの違いに注意してください。

これらすべてのカスタム通知には、ユニバーサル ``_notification``メソッドからアクセスできます。

注釈

"virtual" というラベルの付いたドキュメント内のメソッドも、スクリプトによってオーバーライドされることを目的としています。

典型的な例は、Objectの _init メソッドです。NOTIFICATION_* に相当するものはありませんが、エンジンはメソッドを呼び出します。ほとんどの言語(C#を除く)は、コンストラクタとして使用します。

それでは、これらの通知や仮想機能のそれぞれをどの状況で使用すべきでしょうか?

_process 対 _physics_process 対 *_input

フレームレートに依存するフレーム間のデルタタイムが必要な場合は _process を使用します。オブジェクトデータを更新するコードをできるだけ頻繁に更新する必要がある場合、これが適切な場所です。繰り返しのロジックチェックとデータキャッシングがここで実行されることがよくありますが、更新するために評価が必要になる頻度になります。すべてのフレームを実行する必要がない場合は、Timer-yield-timeoutループを実装することも別のオプションです。

# Infinitely loop, but only execute whenever the Timer fires.
# Allows for recurring operations that don't trigger script logic
# every frame (or even every fixed frame).
while true:
    my_method()
    $Timer.start()
    yield($Timer, "timeout")

フレームレートに依存しないフレーム間のデルタタイムが必要な場合は _physics_process を使用します。時間の進み具合に関係なく、コードの経時的な一貫した更新が必要な場合は、これが適切な場所です。繰り返しのキネマティックおよびオブジェクトの幾何学変換操作をここで実行する必要があります。

最高のパフォーマンスを得るには、これらのコールバック中に入力チェックを行わないようにする必要があります。_process_physics_process はあらゆる機会にトリガーします(デフォルトでは「休息」しません)。対照的に、*_input コールバックは、エンジンが実際に入力を検出したフレームでのみトリガーされます。

同じように、入力コールバック内で入力アクションをチェックできます。デルタ時間を使用する場合、必要に応じて関連するデルタ時間メソッドからそれを取得できます。

# Called every frame, even when the engine detects no input.
func _process(delta):
    if Input.is_action_just_pressed("ui_select"):
        print(delta)

# Called during every input event.
func _unhandled_input(event):
    match event.get_class():
        "InputEventKey":
            if Input.is_action_just_pressed("ui_accept"):
                print(get_process_delta_time())
public class MyNode : Node
{

    // Called every frame, even when the engine detects no input.
    public void _Process(float delta)
    {
        if (Input.IsActionJustPressed("ui_select"))
            GD.Print(delta);
    }

    // Called during every input event. Equally true for _input().
    public void _UnhandledInput(InputEvent event)
    {
        switch (event)
        {
            case InputEventKey keyEvent:
                if (Input.IsActionJustPressed("ui_accept"))
                    GD.Print(GetProcessDeltaTime());
                break;
            default:
                break;
        }
    }

}

_init 対 初期化 対 エクスポート

スクリプトがシーンなしで独自のノードサブツリーを初期化する場合、そのコードはここで実行する必要があります。他のプロパティまたはSceneTreeに依存しない初期化もここで実行する必要があります。これは _ready または _enter_tree の前にトリガーされますが、スクリプトがそのプロパティを作成して初期化した後にトリガーされます。

スクリプトには、インスタンス化の最中に発生する3つのプロパティ割り当てがあります。

# "one" is an "initialized value". These DO NOT trigger the setter.
# If someone set the value as "two" from the Inspector, this would be an
# "exported value". These DO trigger the setter.
export(String) var test = "one" setget set_test

func _init():
    # "three" is an "init assignment value".
    # These DO NOT trigger the setter, but...
    test = "three"
    # These DO trigger the setter. Note the `self` prefix.
    self.test = "three"

func set_test(value):
    test = value
    print("Setting: ", test)
public class MyNode : Node
{
    private string _test = "one";

    // Changing the value from the inspector does trigger the setter in C#.
    [Export]
    public string Test
    {
        get { return _test; }
        set
        {
            _test = value;
            GD.Print("Setting: " + _test);
        }
    }

    public MyNode()
    {
        // Triggers the setter as well
        Test = "three";
    }
}

シーンをインスタンス化する場合、プロパティ値は次の順序に従って設定されます。

  1. 初期値の割り当て: インスタンス化は、初期化値または初期化用の引数値のいずれかを割り当てます。初期化の引数値は初期化値よりも優先されます。
  2. エクスポートされた値の割り当て: スクリプトではなくシーンからインスタンス化する場合、Godotはエクスポートされた値を割り当てて、スクリプトで定義された初期値を置き換えます。

その結果、スクリプトをシーンに対してインスタンス化すると、初期化エンジンがセッターを呼び出す回数の両方に影響します。

_ready 対 _enter_tree 対 NOTIFICATION_PARENTED

最初に実行されたシーンにインスタンス化するシーンを接続すると、Godot はツリーの下にノードをインスタンス化し(_init 呼び出しを行います)、ルートから下に向かってツリーを構築します。これにより、_enter_tree 呼び出しがツリーの下へカスケードされます。ツリーが完成すると、リーフノードは _ready を呼び出します。すべての子ノードの呼び出しが終了すると、ノードはこのメソッドを呼び出します。これにより、逆方向のカスケードがツリーのルートに戻ります。

スクリプトまたはスタンドアロン シーンをインスタンス化する場合、ノードは作成時にSceneTreeに追加されないため、_enter_tree コールバックはトリガーされません。代わりに、_init とその後の _ready 呼び出しのみが発生します。

「メイン/アクティブシーンの一部として発生するかどうかに関係なく、ノードが別のノードの親として発生する動作をトリガーする必要があるばあい、PARENTED 通知を使用できます。たとえば、ノードのメソッドを、失敗せずに親ノードのカスタムシグナルに接続するスニペットを次に示します。実行時に作成されるデータ中心のノードで役立ちます。

extends Node

var parent_cache

func connection_check():
    return parent.has_user_signal("interacted_with")

func _notification(what):
    match what:
        NOTIFICATION_PARENTED:
            parent_cache = get_parent()
            if connection_check():
                parent_cache.connect("interacted_with", self, "_on_parent_interacted_with")
        NOTIFICATION_UNPARENTED:
            if connection_check():
                parent_cache.disconnect("interacted_with", self, "_on_parent_interacted_with")

func _on_parent_interacted_with():
    print("I'm reacting to my parent's interaction!")
public class MyNode : Node
{
    public Node ParentCache = null;

    public void ConnectionCheck()
    {
        return ParentCache.HasUserSignal("InteractedWith");
    }

    public void _Notification(int what)
    {
        switch (what)
        {
            case NOTIFICATION_PARENTED:
                ParentCache = GetParent();
                if (ConnectionCheck())
                    ParentCache.Connect("InteractedWith", this, "OnParentInteractedWith");
                break;
            case NOTIFICATION_UNPARENTED:
                if (ConnectionCheck())
                    ParentCache.Disconnect("InteractedWith", this, "OnParentInteractedWith");
                break;
        }
    }

    public void OnParentInteractedWith()
    {
        GD.Print("I'm reacting to my parent's interaction!");
    }
}