GDScriptスタイルガイド
このスタイルガイドには、エレガントなGDScriptを作成するための規則が記載されています。目標は、クリーンで読みやすいコードの作成を奨励し、プロジェクト、ディスカッション、およびチュートリアル間の一貫性を促進することです。うまくいけば、これも自動フォーマットツールの開発を促進するでしょう。
GDScriptはPythonに近いので、このガイドはPythonの PEP 8 プログラミングスタイルガイドに触発されています。
スタイルガイドは、厳密なルールブックを意味するものではありません。時には以下のガイドラインの一部を適用できない場合があります。その場合は、最善であるように判断を下すと共に、仲間の開発者に洞察を求めてください。
一般に、プロジェクト内およびチーム内でコードの一貫性を保つことは、このガイドに完璧に従うよりも重要です。
注釈
Godotのビルトインスクリプトエディタは、デフォルトでこれらの規則を多く使用します。それをあなたの役に立つようにしてください。
これらのガイドラインに基づいた完全なクラスの例を次に示します:
class_name StateMachine
extends Node
## Hierarchical State machine for the player.
##
## Initializes states and delegates engine callbacks ([method Node._physics_process],
## [method Node._unhandled_input]) to the state.
signal state_changed(previous, new)
@export var initial_state: Node
var is_active = true:
set = set_is_active
@onready var _state = initial_state:
set = set_state
@onready var _state_name = _state.name
func _init():
add_to_group("state_machine")
func _enter_tree():
print("this happens before the ready method!")
func _ready():
state_changed.connect(_on_state_changed)
_state.enter()
func _unhandled_input(event):
_state.unhandled_input(event)
func _physics_process(delta):
_state.physics_process(delta)
func transition_to(target_state_path, msg={}):
if not has_node(target_state_path):
return
var target_state = get_node(target_state_path)
assert(target_state.is_composite == false)
_state.exit()
self._state = target_state
_state.enter(msg)
Events.player_state_changed.emit(_state.name)
func set_is_active(value):
is_active = value
set_physics_process(value)
set_process_unhandled_input(value)
set_block_signals(not value)
func set_state(value):
_state = value
_state_name = _state.name
func _on_state_changed(previous, new):
print("state changed")
state_changed.emit()
class State:
var foo = 0
func _init():
print("Hello!")
書式設定
エンコードと特殊文字
CRLFやCRではなく、改行(LF)文字を使用して改行します。(エディタのデフォルト)
各ファイルの最後に1つの改行文字を使用します。(エディタのデフォルト)
バイトオーダーマークなしでUTF-8エンコーディングを使用します。(エディタのデフォルト)
インデントにはスペースの代わりにタブを使用します。(エディタのデフォルト)
インデント
各インデントレベルは、それを含むブロックより1つ大きくなければなりません。
良い例:
for i in range(10):
print("hello")
悪い例:
for i in range(10):
print("hello")
for i in range(10):
print("hello")
継続行と通常のコードブロックを区別するには、2つのインデントレベルを使用します。
良い例:
effect.interpolate_property(sprite, "transform/scale",
sprite.get_scale(), Vector2(2.0, 2.0), 0.3,
Tween.TRANS_QUAD, Tween.EASE_OUT)
悪い例:
effect.interpolate_property(sprite, "transform/scale",
sprite.get_scale(), Vector2(2.0, 2.0), 0.3,
Tween.TRANS_QUAD, Tween.EASE_OUT)
この規則の例外は、配列、辞書、および列挙型です。単一のインデントレベルを使用して、継続行を区別します。
良い例:
var party = [
"Godot",
"Godette",
"Steve",
]
var character_dict = {
"Name": "Bob",
"Age": 27,
"Job": "Mechanic",
}
enum Tile {
BRICK,
FLOOR,
SPIKE,
TELEPORT,
}
悪い例:
var party = [
"Godot",
"Godette",
"Steve",
]
var character_dict = {
"Name": "Bob",
"Age": 27,
"Job": "Mechanic",
}
enum Tile {
BRICK,
FLOOR,
SPIKE,
TELEPORT,
}
末尾のコンマ
配列、辞書、および列挙型の最後の行に末尾のコンマを使用します。これにより、新しい要素を追加するときに最後の行を変更する必要がないため、リファクタリングが容易になり、バージョン管理の差分が改善されます。
良い例:
var array = [
1,
2,
3,
]
悪い例:
var array = [
1,
2,
3
]
単一行リストでは末尾のコンマは不要なので、この場合は追加しないでください。
良い例:
var array = [1, 2, 3]
悪い例:
var array = [1, 2, 3,]
空白行
関数とクラスの定義は2つの空白行で囲んで下さい:
func heal(amount):
health += amount
health = min(health, max_health)
health_changed.emit(health)
func take_damage(amount, effect=null):
health -= amount
health = max(0, health)
health_changed.emit(health)
論理セクションを区切るには、関数内で1行の空白行を使用します。
注釈
クラスリファレンスとこのドキュメントの短いコードスニペットでは、クラスと関数定義の間の空行は1行にしています。
行の長さ
コードの個々の行を100文字以内に保ちます。
可能であれば、行を80文字未満に保つようにしてください。これにより、小さなディスプレイでコードを読む場合や、2つのスクリプトを外部テキスト エディタで並べて開くときに役立ちます。たとえば、差分リビジョンを見る場合などです。
1行につき1つのステートメント
読みやすさのためのGDScriptスタイル ガイドラインに準拠するために、条件ステートメントを含む複数のステートメントを1行に結合することは避けてください。
良い例:
if position.x > width:
position.x = 0
if flag:
print("flagged")
悪い例:
if position.x > width: position.x = 0
if flag: print("flagged")
その規則の唯一の例外は三項演算子です:
next_state = "idle" if is_on_floor() else "fall"
可読性のための複数行ステートメント形式
特に長い if ステートメントやネストされた三項式がある場合、それらを複数行で記述すると読みやすくなります。継続行も同じ式の一部であるため、1つのインデントレベルではなく2つのインデントレベルを使用する必要があります。
GDScriptでは括弧またはバックスラッシュを使用して複数行のステートメントをラップできます。このスタイルガイドではリファクタリングが容易になるため、括弧が好まれています。バックスラッシュを使用する場合は、最終行の末尾にバックスラッシュが決して含まれないようにする必要があります。括弧を使用すると最後の行の最後にバックスラッシュがあることを心配する必要はありません。
条件式が複数行にまたがるとき、キーワード and/or は前の行の末尾ではなく、継続する行の先頭に配置する必要があります。
良い例:
var angle_degrees = 135
var quadrant = (
"northeast" if angle_degrees <= 90
else "southeast" if angle_degrees <= 180
else "southwest" if angle_degrees <= 270
else "northwest"
)
var position = Vector2(250, 350)
if (
position.x > 200 and position.x < 400
and position.y > 300 and position.y < 400
):
pass
悪い例:
var angle_degrees = 135
var quadrant = "northeast" if angle_degrees <= 90 else "southeast" if angle_degrees <= 180 else "southwest" if angle_degrees <= 270 else "northwest"
var position = Vector2(250, 350)
if position.x > 200 and position.x < 400 and position.y > 300 and position.y < 400:
pass
不要なかっこ()を入れない
式や条件文に括弧を付けないでください。 操作の順序や複数行の折り返しに必要な場合を除き、可読性が低下するだけです。
良い例:
if is_colliding():
queue_free()
悪い例:
if (is_colliding()):
queue_free()
ブール演算子
最もアクセスしやすいように、ブール演算子のプレーンな英語バージョンを優先します:
&&の代わりにandを使用します。||の代わりにorを使用します。!の代わりにnotを使用します。
また、ブール演算子をかっこで囲んであいまいさを解消することもできます。これにより、長い式が読みやすくなります。
良い例:
if (foo and bar) or not baz:
print("condition is true")
悪い例:
if foo && bar || !baz:
print("condition is true")
空白
演算子の周囲とカンマの後には、必ずスペースを1つ追加してください。また辞書参照や関数呼び出しでは余分なスペースを避けてください。例外の1つは単一行の辞書宣言の場合で、左中括弧の後と右中括弧の前にスペースを追加します。これによりほとんどのフォントで [] 文字が {} に近いように見えるときでも、辞書と配列を視覚的に区別しやすくなります。
良い例:
position.x = 5
position.y = target_position.y + 10
dict["key"] = 5
my_array = [4, 5, 6]
my_dictionary = { key = "value" }
print("foo")
悪い例:
position.x=5
position.y = mpos.y+10
dict ["key"] = 5
myarray = [4,5,6]
my_dictionary = {key = "value"}
print ("foo")
式を垂直方向に揃えるためにスペースを使用しないでください:
x = 100
y = 100
velocity = 500
引用符
シングルクォートを使えばエスケープする文字数を減らせる場合以外は、なるべくダブルクォートを使用するようにします。下記の例を見てください。
# Normal string.
print("hello world")
# Use double quotes as usual to avoid escapes.
print("hello 'world'")
# Use single quotes as an exception to the rule to avoid escapes.
print('hello "world"')
# Both quote styles would require 2 escapes; prefer double quotes if it's a tie.
print("'hello' \"world\"")
数字
浮動小数点数の先頭または末尾のゼロを省略しないでください。そうしないと可読性が低くなり、一目で整数と区別するのが難しくなります。
良い例:
var float_number = 0.234
var other_float_number = 13.0
悪い例:
var float_number = .234
var other_float_number = 13.
16進数の文字には小文字を使用します。高さが低くなり数字が読みやすくなります。
良い例:
var hex_number = 0xfb8c0b
悪い例:
var hex_number = 0xFB8C0B
リテラル数値ではGDScriptのアンダースコアを利用して、大きな数値を読みやすくします。
良い例:
var large_number = 1_234_567_890
var large_hex_number = 0xffff_f8f8_0000
var large_bin_number = 0b1101_0010_1010
# Numbers lower than 1000000 generally don't need separators.
var small_number = 12345
悪い例:
var large_number = 1234567890
var large_hex_number = 0xfffff8f80000
var large_bin_number = 0b110100101010
# Numbers lower than 1000000 generally don't need separators.
var small_number = 12_345
命名規則
These naming conventions follow the Godot Engine style. Breaking these will make your code clash with the built-in naming conventions, leading to inconsistent code. As a summary table:
タイプ(型) |
Convention |
サンプル |
|---|---|---|
ファイル名 |
snake_case |
|
Class names |
PascalCase |
|
Node names |
PascalCase |
|
関数 |
snake_case |
|
変数 |
snake_case |
|
シグナル |
snake_case |
|
定数 |
CONSTANT_CASE |
|
Enum names |
PascalCase |
|
Enum members |
CONSTANT_CASE |
|
ファイル名
ファイル名にはsnake_caseを使用してください。名前付きクラスはPascalCaseクラス名をsnake_caseに変換します:
# This file should be saved as `weapon.gd`.
class_name Weapon
extends Node
# This file should be saved as `yaml_parser.gd`.
class_name YAMLParser
extends Object
これはGodotのソース コードのC++ファイルの名前の付け方と一致しています。これによりプロジェクトをWindowsから他のプラットフォームにエクスポートするときに発生する可能性がある大文字と小文字の区別の問題が回避されます。
クラスとノード
クラス名とノード名には PascalCase を使用します:
extends CharacterBody3D
クラスを定数または変数にロードするときにもPascalCaseを使用します:
const Weapon = preload("res://weapon.gd")
関数と変数
関数と変数に名前を付けるには、snake_caseを使用します:
var particle_effect
func load_level():
ユーザーがオーバーライドする必要がある仮想メソッド関数、プライベート関数、およびプライベート変数の前に単一のアンダースコア(_)を追加します:
var _counter = 0
func _recalculate_path():
シグナル
過去形を使用してシグナルに名前を付けます:
signal door_opened
signal score_changed
定数と列挙型
CONSTANT_CASEで定数を記述します。つまり、すべて大文字でアンダースコア(_)を使用して単語を区切ります:
const MAX_SPEED = 200
Use PascalCase for enum names and keep them singular, as they represent a type. Use CONSTANT_CASE for their members, as they are constants:
enum Element {
EARTH,
WATER,
AIR,
FIRE,
}
Write enums with each item on its own line. This allows adding documentation comments above each item more easily, and also makes for cleaner diffs in version control when items are added or removed.
良い例:
enum Element {
EARTH,
WATER,
AIR,
FIRE,
}
悪い例:
enum Element { EARTH, WATER, AIR, FIRE }
コードの順序
This section focuses on code order. For formatting, see 書式設定. For naming conventions, see 命名規則.
次のようにGDScriptコードを整理することをお勧めします:
01. @tool, @icon, @static_unload
02. class_name
03. extends
04. ## doc comment
05. signals
06. enums
07. constants
08. static variables
09. @export variables
10. remaining regular variables
11. @onready variables
12. _static_init()
13. remaining static methods
14. overridden built-in virtual methods:
1. _init()
2. _enter_tree()
3. _ready()
4. _process()
5. _physics_process()
6. remaining virtual methods
15. overridden custom methods
16. remaining methods
17. subclasses
And put the class methods and variables in the following order depending on their access modifiers:
1. public
2. private
順序を最適化して、コードを上から下に読みやすくし、コードを初めて読む開発者がどのように機能するかを理解しやすくし、変数宣言の順序に関連するエラーを回避します。
このコードの順序は、次の4つの経験則に従います:
プロパティとシグナルが最初に来て、メソッドがそれに続きます。
パブリックはプライベートの前に来ます。
仮想コールバックは、クラスのインターフェースの前に来ます。
オブジェクトの構築関数と初期化関数、
_initと_readyは実行時にオブジェクトを変更する関数の前に来ます。
クラス宣言
コードがエディタで実行されることを意図している場合、スクリプトの最初の行に @tool アノテーションを置きます。
Follow with the optional @icon then the class_name if necessary. You can turn a
GDScript file into a global type in your project using class_name. For more
information, see クラスに名前を付けて登録する.
次に、クラスが組み込み型を拡張する場合は、extends キーワードを追加します。
それに続いてクラスのオプションの ドキュメント コメント が必要になります。これを使用して、たとえばクラスの役割、クラスの仕組み、他の開発者がクラスをどのように使用する必要があるかをチームメイトに説明できます。
class_name MyNode
extends Node
## A brief description of the class's role and functionality.
##
## The description of the script, what it can do,
## and any further detail.
シグナルとプロパティ
追加ドキュメントの後に、シグナル宣言、続いてプロパティ、つまりメンバー変数を記述します。
列挙型は、他のプロパティのエクスポートヒントとして使用できるため、シグナルの後に来る必要があります。
次に、定数、エクスポートされた変数、public、private、およびonready変数をこの順序で記述します。
signal player_spawned(position)
enum Job {
KNIGHT,
WIZARD,
ROGUE,
HEALER,
SHAMAN,
}
const MAX_LIVES = 3
@export var job: Job = Job.KNIGHT
@export var max_health = 50
@export var attack = 5
var health = max_health:
set(new_health):
health = new_health
var _speed = 300.0
@onready var sword = get_node("Sword")
@onready var gun = get_node("Gun")
注釈
GDScript は _ready コールバックの直前に @onready 変数を評価します。これを使用してノードの依存関係をキャッシュしたり、クラスが依存しているシーン内の子ノードを取得したりできます。これは上記の例が示すものです。
メンバ変数
メンバ変数がメソッド内でローカルでのみ使用される場合は、コードを理解することが難しくなるため、メンバ変数を宣言しないでください。その代わりにメソッドの中でローカル変数として宣言します。
ローカル変数
ローカル変数はできるだけ最初に使用する直前に宣言してください。これにより変数が宣言されている場所を見つけるためにスクロールしすぎずに、コードをたどるのが簡単になります。
メソッドと静的関数
クラスのプロパティの後にメソッドが続きます。
エンジンがメモリ内にオブジェクトを作成するときに呼び出す _init() コールバックメソッドで開始します。 そしてGodotがノードをシーンツリーに追加するときに呼び出す _ready() コールバックを続けます。
これらの関数はオブジェクトのどのように初期化されるかを示すため、最初に配置する必要があります。
_unhandled_input() や _physics_process のような、その他の組み込みの仮想コールバックが次に来る必要があります。これらは、オブジェクトのメインループとゲームエンジンとの相互作用を制御します。
クラスの残りのインターフェイスであるパブリックメソッドとプライベートメソッドは、この順序でその後に続きます。
func _init():
add_to_group("state_machine")
func _ready():
state_changed.connect(_on_state_changed)
_state.enter()
func _unhandled_input(event):
_state.unhandled_input(event)
func transition_to(target_state_path, msg={}):
if not has_node(target_state_path):
return
var target_state = get_node(target_state_path)
assert(target_state.is_composite == false)
_state.exit()
self._state = target_state
_state.enter(msg)
Events.player_state_changed.emit(_state.name)
func _on_state_changed(previous, new):
print("state changed")
state_changed.emit()
静的型付け
GDScript supports optional static typing.
宣言された型
変数の型を宣言するには、 <variable>: <type> を使用します。
var health: int = 0
関数の戻り値の定義をするには、``-> <type>``を使ってください:
func heal(amount: int) -> void:
推論された型
In most cases, you can let the compiler infer the type using :=.
Prefer := when the type is written on the same line as the assignment,
otherwise prefer writing the type explicitly.
良い例:
# The type can be int or float, and thus should be stated explicitly.
var health: int = 0
# The type is clearly inferred as Vector3.
var direction := Vector3(1, 2, 3)
型があいまいな場合は型ヒントを含め、冗長な場合は型ヒントを省略します。
悪い例:
# Typed as int, but it could be that float was intended.
var health := 0
# The type hint has redundant information.
var direction: Vector3 = Vector3(1, 2, 3)
# What type is this? It's not immediately clear to the reader, so it's bad.
var value := complex_function()
場合によっては型を明示的に指定する必要があります。明示的に指定しないとコンパイラは関数の戻り値の型しか使用できないため、動作が期待どおりになりません。たとえばノードのシーンまたはファイルがメモリにロードされない限り、 get_node() は型を推論できません。この場合、型を明示的に設定する必要があります。
良い例:
@onready var health_bar: ProgressBar = get_node("UI/LifeBar")
悪い例:
# The compiler can't infer the exact type and will use Node
# instead of ProgressBar.
@onready var health_bar := get_node("UI/LifeBar")
あるいは as キーワードを使用して戻り値の型をキャストすることもでき、その型は var の型を推測するために使用されます。
@onready var health_bar := get_node("UI/LifeBar") as ProgressBar
# health_bar will be typed as ProgressBar
注釈
This option is considered more type-safe than type hints,
but also less null-safe as it silently casts the variable to null in case of a type mismatch at runtime,
without an error/warning.
コメントスペース
通常のコメント (
#) とドキュメント コメント (##) はスペースで始める必要がありますが、コメントアウトしたコードはスペースを避けてください。さらにコード領域のコメント (#region/#endregion) はその正確な構文に従う必要があるため、スペースを含めないでください。通常のコメントとドキュメントコメントにスペースを付けると、テキストコメントと無効なコードを区別するのに役立ちます。
良い例:
悪い例:
注釈
スクリプトエディタで、選択したコードのコメントを切り替えるには、Ctrl + K を押します。この機能は、選択した行のコードの前に単一の#記号を追加、または削除します。
Prefer writing comments on their own line as opposed to inline comments (comments written on the same line as code). Inline comments are best used for short comments, typically a few words at most:
良い例:
悪い例: