They create their first scene and fill it with content only to eventually end up saving branches of their scene into separate scenes as the nagging feeling that they should split things up starts to accumulate. However, they then notice that the hard references they were able to rely on before are no longer possible. Re-using the scene in multiple places creates issues because the node paths do not find their targets and signal connections established in the editor break.


One of the biggest things to consider in OOP is maintaining focused, singular-purpose classes with loose coupling to other parts of the codebase. This keeps the size of objects small (for maintainability) and improves their reusability.

These OOP best practices have several implications for best practices in scene structure and script usage.

可能な限り、依存関係を持たないようにシーンを設計する必要があります。 つまり、必要なものすべてを内部に保持するシーンを作成する必要があります。

シーンが外部コンテキストとやり取りする必要がある場合、経験豊富な開発者は、 依存性の注入 <https://en.wikipedia.org/wiki/Dependency_injection> の使用をお勧めします。この手法では、高レベルAPIを使用して低レベルAPIの依存関係を提供します。なぜこれを行うのですか?外部環境に依存するクラスは、誤ってバグや予期しない動作を引き起こす可能性があるためです。


  1. Connect to a signal. Extremely safe, but should be used only to "respond" to behavior, not start it. Note that signal names are usually past-tense verbs like "entered", "skill_activated", or "item_collected".

    # Parent
    $Child.connect("signal_name", object_with_method, "method_on_the_object")
    # Child
    emit_signal("signal_name") # Triggers parent-defined behavior.
  2. メソッドを呼び出します。動作を開始するために使用されます。

    # Parent
    $Child.method_name = "do"
    # Child, assuming it has String property 'method_name' and method 'do'.
    call(method_name) # Call parent-defined method (which child must own).
  3. FuncRef プロパティを初期化します。メソッドの所有権は不要なので、メソッドよりも安全です。動作を開始するために使用されます。

    # Parent
    $Child.func_property = funcref(object_with_method, "method_on_the_object")
    # Child
    func_property.call_func() # Call parent-defined method (can come from anywhere).
  4. ノードまたはその他のオブジェクト参照を初期化します。

    # Parent
    $Child.target = self
    # Child
    print(target) # Use parent-defined node.
  5. NodePathを初期化します。

    # Parent
    $Child.target_path = ".."
    # Child
    get_node(target_path) # Use parent-defined NodePath.

These options hide the points of access from the child node. This in turn keeps the child loosely coupled to its environment. One can re-use it in another context without any extra changes to its API.



# Parent
$Left.target = $Right.get_node("Receiver")

# Left
var target: Node
func execute():
    # Do something with 'target'.

# Right
func _init():
    var receiver = Receiver.new()




そのようなドキュメントの作成と保守を回避するために、依存ノード (上記の "child") を _get_configuration_warning() を実装するツールスクリプトに変換します。空でない文字列を返すと、シーンドックは、ノードのツールチップとして文字列を持つ警告アイコンを生成します。これは、子 CollisionShape2D ノードが定義されていない場合に、Area2D ノードなどのノードに対して表示されるアイコンと同じです。次に、エディタはスクリプト コードを使用してシーンを自己ドキュメント化します。ドキュメントによるコンテンツの複製は必要ありません。


So, why do all this complex switcharoo work? Well, because scenes operate best when they operate alone. If unable to work alone, then working with others anonymously (with minimal hard dependencies, i.e. loose coupling) is the next best thing. Inevitably, changes may need to be made to a class and if these changes cause it to interact with other scenes in unforeseen ways, then things will start to break down. The whole point of all this indirection is to avoid ending up in a situation where changing one class results in adversely effecting other classes.

Scripts and scenes, as extensions of engine classes, should abide by all OOP principles. Examples include...


So, a developer starts work on a game only to stop at the vast possibilities before them. They might know what they want to do, what systems they want to have, but where to put them all? Well, how one goes about making their game is always up to them. One can construct node trees in countless ways. But, for those who are unsure, this helpful guide can give them a sample of a decent structure to start with.

A game should always have a sort of "entry point"; somewhere the developer can definitively track where things begin so that they can follow the logic as it continues elsewhere. This place also serves as a bird's eye view of all of the other data and logic in the program. For traditional applications, this would be the "main" function. In this case, it would be a Main node.

  • Node "Main" (main.gd)

main.gd スクリプトは、ゲームのプライマリコントローラーとして機能します。


  • Node "Main" (main.gd)
    • Node2D/Spatial "World" (game_world.gd)

    • Control "GUI" (gui.gd)

レベルを変更する場合、"World"ノードの子を入れ替えることができます。シーンを手動で変更 を使用すると、ユーザーはゲームのワールドがどのように遷移するかを完全に制御できます。


  1. すべてのデータを内部的に追跡する

  2. グローバルにアクセス可能でなければなりません

  3. 独立して存在する必要があります

...次に、自動ロード 'シングルトン' ノード を作成する必要があります。


小規模なゲームの場合、コントロールの少ない簡単な代替手段は、単に SceneTree.change_scene() メソッドを呼び出してメインシーンのコンテンツをスワップする「ゲーム(Game)」シングルトンにすることです。この構造により、「ワールド(World)」がメインゲームノードとして保持されます。

Any GUI would need to also be a singleton; be a transitory part of the "World"; or be manually added as a direct child of the root. Otherwise, the GUI nodes would also delete themselves during scene transitions.

If one has systems that modify other systems' data, one should define those as their own scripts or scenes rather than autoloads. For more information on the reasons, please see the Autoloads versus regular nodes documentation.



場合によっては、これらの分離されたノードが お互い を相対的に配置する必要があります。このために:ref:RemoteTransform <class_RemoteTransform> /RemoteTransform2D ノードを使用できます。ターゲットノードは、Remote*ノードから選択した幾何学変換の要素を条件付きで継承できます。target NodePath を割り当てるには、次のいずれかを使用します:

  1. 割り当てを仲介する信頼できるサードパーティ(おそらく親ノード)。

  2. 目的のノードへの参照を簡単にプルするためのグループ(ターゲットの1つだけが存在すると想定)。

When should one do this? Well, this is subjective. The dilemma arises when one must micro-manage when a node must move around the SceneTree to preserve itself. For example...

  • 「プレイヤー」ノードを「ルーム」に追加する。

  • 部屋を変更する必要があるため、現在の部屋を削除する必要があり。

  • ルームを削除する前に、プレイヤーを保存および/または移動する必要があります。


    • そうでない場合は、2つの部屋を作成し、プレイヤーを移動して古い部屋を削除するだけです。特に心配はいりません。


    • プレイヤーをツリー内の別の場所に移動します。

    • 部屋を削除します。

    • 新しい部屋をインスタンス化して追加します。

    • そこにプレイヤーを再度追加します。

The issue is that the player here is a "special case"; one where the developers must know that they need to handle the player this way for the project. As such, the only way to reliably share this information as a team is to document it. Keeping implementation details in documentation however is dangerous. It's a maintenance burden, strains code readability, and bloats the intellectual content of a project unnecessarily.

In a more complex game with larger assets, it can be a better idea to simply keep the player somewhere else in the SceneTree entirely. This results in:

  1. より一貫性が高くなります。

  2. どこかに文書化して保守する必要がある「特別なケース」はありません。

  3. これらの詳細を考慮する必要がないため、エラーが発生する可能性はありません。


  1. declarative ソリューション: それらの間に Node を配置します。幾何学変換を行わないノードとして、ノードはそのような情報を子に渡しません。

  2. imperative ソリューション: CanvasItem ノードまたは Spatial ノードに set_as_toplevel セッターを使用します。これにより、ノードは継承された幾何学変換を無視します。



The key to scene organization is to consider the SceneTree in relational terms rather than spatial terms. Are the nodes dependent on their parent's existence? If not, then they can thrive all by themselves somewhere else. If they are dependent, then it stands to reason that they should be children of that parent (and likely part of that parent's scene if they aren't already).

これは、ノード自体が構成要素であることを意味しますか? 全く違います。Godotのノードツリーは、構図ではなく集合の関係性を形成します。ただし、ノードを自由に移動できる柔軟性はありますが、デフォルトではそのような移動が不要な場合に最適です。