Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

ロジックの設定

問題Xにアプローチするときに、戦略をYにするかZするかについて悩んだことはありませんか?この記事では、これらのジレンマに関連するさまざまなトピックについて説明します。

Adding nodes and changing properties: which first?

When initializing nodes from a script at runtime, you may need to change properties such as the node's name or position. A common dilemma is, when should you change those values?

It is the best practice to change values on a node before adding it to the scene tree. Some property's setters have code to update other corresponding values, and that code can be slow! For most cases, this code has no impact on your game's performance, but in heavy use cases such as procedural generation, it can bring your game to a crawl.

For these reasons, it is always a best practice to set the initial values of a node before adding it to the scene tree.

ロード(load) 対 プリロード(preload)

GDScript には、グローバル preload メソッドが存在します。"読み込み" 処理を前もって行い、パフォーマンスに敏感なコードの途中で、リソースの読み込が発生することを回避するために、できるだけ早くリソースを読み込みます。

Its counterpart, the load method, loads a resource only when it reaches the load statement. That is, it will load a resource in-place which can cause slowdowns when it occurs in the middle of sensitive processes. The load() function is also an alias for ResourceLoader.load(path) which is accessible to all scripting languages.

それでは、load と比較して preload が発生するのは正確にはいつですか?例を見てみましょう:

# my_buildings.gd
extends Node

# Note how constant scripts/scenes have a different naming scheme than
# their property variants.

# This value is a constant, so it spawns when the Script object loads.
# The script is preloading the value. The advantage here is that the editor
# can offer autocompletion since it must be a static path.
const BuildingScn = preload("res://building.tscn")

# 1. The script preloads the value, so it will load as a dependency
#    of the 'my_buildings.gd' script file. But, because this is a
#    property rather than a constant, the object won't copy the preloaded
#    PackedScene resource into the property until the script instantiates
#    with .new().
#
# 2. The preloaded value is inaccessible from the Script object alone. As
#    such, preloading the value here actually does not benefit anyone.
#
# 3. Because the user exports the value, if this script stored on
#    a node in a scene file, the scene instantiation code will overwrite the
#    preloaded initial value anyway (wasting it). It's usually better to
#    provide null, empty, or otherwise invalid default values for exports.
#
# 4. It is when one instantiates this script on its own with .new() that
#    one will load "office.tscn" rather than the exported value.
export(PackedScene) var a_building = preload("office.tscn")

# Uh oh! This results in an error!
# One must assign constant values to constants. Because `load` performs a
# runtime lookup by its very nature, one cannot use it to initialize a
# constant.
const OfficeScn = load("res://office.tscn")

# Successfully loads and only when one instantiates the script! Yay!
var office_scn = load("res://office.tscn")

preload により、スクリプトは、スクリプトを読み込んだ瞬間にすべての読み込みを処理できます。preload は便利ですが、それを望まない場合もあります。これらの状況を区別するために、考慮できることがいくつかあります。

  1. スクリプトがいつロードされるかを判断できない場合、リソース、特にシーンまたはスクリプトをプリロードすると、予期しない追加のロードが発生する可能性があります。これにより、元のスクリプトのロード操作に加えて、意図しない可変長のロード時間が発生する可能性があります。

  2. シーンのエクスポートされた初期化など、他の何かが値を置き換えることができる場合、値をプリロードしても意味がありません。スクリプトを常に独自に作成する場合、この点は重要な要素ではありません。

  3. If one wishes only to 'import' another class resource (script or scene), then using a preloaded constant is often the best course of action. However, in exceptional cases, one may wish not to do this:

    1. If the 'imported' class is liable to change, then it should be a property instead, initialized either using an export or a load() (and perhaps not even initialized until later).

    2. If the script requires a great many dependencies, and one does not wish to consume so much memory, then one may wish to, load and unload various dependencies at runtime as circumstances change. If one preloads resources into constants, then the only way to unload these resources would be to unload the entire script. If they are instead loaded properties, then one can set them to null and remove all references to the resource entirely (which, as a RefCounted-extending type, will cause the resources to delete themselves from memory).

大きなレベル(ステージ): 静的レベル 対 動的レベル

大きなレベルを作成している場合、どの状況が最も適切ですか?レベルを1つの静的な空間として作成する必要がありますか?または、レベルを断片的にロードし、必要に応じてワールドのコンテンツをシフトする必要がありますか?

Well, the simple answer is, "when the performance requires it." The dilemma associated with the two options is one of the age-old programming choices: does one optimize memory over speed, or vice versa?

素朴な答えは、すべてを一度にロードする静的レベルを使用することです。ただし、プロジェクトによっては、大量のメモリを消費する可能性があります。ユーザーのRAMを浪費すると、コンピュータが同時に実行しようとする他のすべてのプログラムが、低速化または完全にクラッシュしてしまいます。

何があっても、大きなシーンを小さなシーンに分割する必要があります(資産の再利用性を高めるため)。開発者は、リソースとノードの作成/ロード、削除/アンロードをリアルタイムで管理するノードを設計できます。大規模で多様な環境や手続き的に生成された要素を持つゲームは、多くの場合、メモリの無駄を避けるためにこれらの戦略を実装します。

裏を返せば、動的システムのコーディングはより複雑です。つまり、より多くのプログラムされたロジックを使用するため、エラーやバグが発生する可能性があります。注意しないと、アプリケーションの技術的負債を膨らませるシステムを開発してしまいます。

そのため、最良のオプションは...

  1. 小さなゲームには静的レベルを使用する。

  2. 中規模または大規模のゲームでは、時間/リソースが確保できる場合は、ノードとリソースの管理をコーディングできるライブラリまたはプラグインを作成します。使いやすさと安定性を改善するために時間の経過とともに改良されれば、プロジェクト全体で信頼性の高いツールに進化する可能性があります。

  3. コーディングスキルはあるが、コードを絞り込むための時間やリソースは持っていないため、中規模/大規模ゲームの動的ロジックをコーディングします(ゲームを完成させる必要があるので)。後でリファクタリングして、コードをプラグインにアウトソースできます。

実行時にシーンを入れ替えるさまざまな方法の例については、「手動でシーンを変更する」 ドキュメントを参照してください。