Godotのシーンとスクリプトはクラスです

Godotでは、スクリプトとシーンの両方をオブジェクト指向プログラミング言語のクラスに相当させることができます。主な違いは、シーンは 宣言型コード<https://en.wikipedia.org/wiki/Declarative_programming> であるのに対して、スクリプトには 命令型コード<https://en.wikipedia.org/wiki/Imperative_programming> を含めることができることです。

As a result, many best practices in Godot boil down to applying Object-Oriented design principles to the scenes, nodes, or scripts that make up your game.

このガイドでは、スクリプトとシーンがエンジンのコアでどのように機能するかを説明し、Godotが内部でどのように機能するかを理解し、このシリーズのベストプラクティスの一部を理解するのに役立ちます。

Godotのクラスを理解する

Godotエンジンには node のような組み込みクラスが用意されています。ユーザーが作成した型は、技術的にはクラスではありません。そうではなく、エンジンの組み込みクラスの 1つで実行する初期化のシーケンスをエンジンに指示するリソースです。

Godotの内部クラスには、クラスのデータを ClassDB に登録するメソッドがあります。このデータベースは、クラス情報へのランタイムアクセスを提供します。ClassDB には、次のようなクラスに関する情報が含まれています。

  • プロパティ
  • メソッド
  • 定数
  • シグナル

この ClassDB は、プロパティへのアクセスやメソッドの呼び出しなどの操作を実行するときにオブジェクトがチェックするものです。ClassDB は、データベースのレコードとオブジェクトの基本型のレコードをチェックして、オブジェクトが操作をサポートしているかどうかを確認します。

エンジン側では、すべてのクラスが静的な _bind_methods() 関数を定義し、データベースに登録するC++コンテンツとその方法を記述します。エンジンを使用する場合、ノードに Script をアタッチすることによりClassDB から利用可能なメソッド、プロパティ、シグナルを拡張できます。

オブジェクトは、データベースより以前にアタッチされたスクリプトをチェックします。このため、スクリプトは組み込みメソッドをオーバーライドできます。スクリプトで _get_property_list() メソッドが定義されている場合、GodotはオブジェクトがClassDBから取得するプロパティのリストにそのデータを追加します。同じことが他の宣言型コードにも当てはまります。

組み込み型から継承しないスクリプト、つまり extends キーワードで始まらないスクリプトでさえ、エンジンのベース Reference クラスから暗黙的に継承します。これにより、オブジェクトは、エンジンロジックが適切と考えるスクリプトのコンテンツに従うことができます。

注釈

その結果、コードから extends キーワードを指定せずにスクリプトをインスタンス化することはできますが、Node にアタッチすることはできません

スクリプトのパフォーマンスとPackedScene

オブジェクトのサイズが大きくなるにつれて、作成に必要なスクリプトのサイズがどんどん大きくなります。ノード階層を作成すると、これが実証されます。個々のノードのロジックは、長さが数百行のコードになる可能性があります。

単一の Node を子として作成する簡単な例を見てみましょう。以下のコードは、新しい Node を作成し、その名前を変更し、スクリプトをそれに割り当て、将来の親(Main)を所有者として設定し、それとともにディスクに保存され、最後に ``Main``ノードの子として追加します:

# Main.gd
extends Node

func _init():
    var child = Node.new()
    child.name = "Child"
    child.script = preload("Child.gd")
    child.owner = self
    add_child(child)
using System;
using Godot;

namespace ExampleProject
{
    public class Main : Resource
    {
        public Node Child { get; set; }

        public Main()
        {
            Child = new Node();
            Child.Name = "Child";
            Child.Script = (Script)ResourceLoader.Load("child.gd");
            Child.Owner = this;
            AddChild(Child);
        }
    }
}

このようなスクリプト コードは、エンジン側の C++コードよりもはるかに低速です。各変更は、実行するロジックを見つけるためにバックエンドで多くの「ルックアップ」につながるスクリプトAPIを個別に呼び出します。

シーンは、このパフォーマンスの問題を回避するのに役立ちます。PackedScene は、シーンが継承する基本型であり、シリアル化されたデータを使用してオブジェクトを作成するリソースです。エンジンは、バックエンドでシーンをバッチ処理し、スクリプトよりもはるかに優れたパフォーマンスを提供できます。

シーンとスクリプトはオブジェクト

シーン編成にとって、これが重要なのはなぜですか? なぜなら、シーンオブジェクトだからです。サブノードを利用するスクリプト化されたルートノードとシーンをペアにすることがよくあります。これは、シーンがスクリプトの宣言コードの拡張であることが多いことを意味します。

シーンのコンテンツは、以下を定義するのに役立ちます:

  • スクリプトで使用可能なノード
  • どのように構成されているか
  • How they are initialized
  • それら相互のシグナル接続

記述されたコードに適用される多くのオブジェクト指向の原則は、シーンにも適用されます。

シーンは 常にルートノードにアタッチされたスクリプトの拡張 です 。1つのクラスの一部として、そのノードに含まれるすべてのノードを確認できます。

このシリーズで説明するヒントとテクニックのほとんどは、これに基づいています。