静的型付けGDScript

このガイドでは、次の内容について学習します:

  • GDScriptで型を使用する方法
  • 静的型はバグの回避に役立ちます

この新しい言語機能をどこで、どのように使うかは完全にあなた次第です!

静的型は、変数、定数、関数、パラメータ、戻り値の型に使用できます。

注釈

型付きGDScriptはGodot 3.1から使用できます。

静的型付けの概要

With typed GDScript, Godot can detect even more errors as you write code! It gives you and your teammates more information as you're working, as the arguments' types show up when you call a method.

Imagine you're programming an inventory system. You code an Item node, then an Inventory. To add items to the inventory, the people who work with your code should always pass an Item to the Inventory.add method. With types, you can enforce this:

# In 'Item.gd'.
class_name Item
# In 'Inventory.gd'.
class_name Inventory


func add(reference: Item, amount: int = 1):
    var item = find_item(reference)
    if not item:
        item = _instance_item_from_db(reference)

    item.amount += amount

型付きGDScriptのもう1つの重要な利点は、新しい警告システムです。バージョン3.1以降のGodotではコードを記述する際に警告が表示されます: 実行時に問題を引き起こす可能性のあるコードのセクションが識別されますが、コードをそのままにしておくかどうかを決めることができます。これについては後で説明します。

Static types also give you better code completion options. Below, you can see the difference between a dynamic and a static typed completion options for a class called PlayerController.

You've probably stored a node in a variable before, and typed a dot to be left with no autocomplete suggestions:

code completion options for dynamic

This is due to dynamic code. Godot cannot know what node or value type you're passing to the function. If you write the type explicitly however, you will get all public methods and variables from the node:

code completion options for typed

将来的には、型付きGDScriptによってコードのパフォーマンスも向上します。ジャストインタイムコンパイルやその他のコンパイラの改善はすでにロードマップに盛り込まれています!

Overall, typed programming gives you a more structured experience. It helps prevent errors and improves the self-documenting aspect of your scripts. This is especially helpful when you're working in a team or on a long-term project: studies have shown that developers spend most of their time reading other people's code, or scripts they wrote in the past and forgot about. The clearer and the more structured the code, the faster it is to understand, the faster you can move forward.

静的型付けの使用方法

To define the type of a variable or a constant, write a colon after the variable's name, followed by its type. E.g. var health: int. This forces the variable's type to always stay the same:

var damage: float = 10.5
const MOVE_SPEED: float = 50.0

コロンを書くと、Godotは型を推測しようとしますが、型を省略します:

var life_points := 4
var damage := 10.5
var motion := Vector2()

今、使えるのは3種類の型です:

  1. 組み込み型
  2. コアクラスとノード ( ObjectNodeArea2DCamera2D など)
  3. 独自のカスタムクラス。新しい class_name 機能を調べて、型をエディタに登録します。

注釈

Godotは割り当てられた値から自動的に型を設定するので、定数の型ヒントを書く必要はありません。ただしコードの意図をより明確にするために、これを行うこともできます。

カスタム変数型

カスタムクラスを含む任意のクラスを型として使用できます。スクリプトで使用する方法は2つあります。最初の方法は、定数の型として使用するスクリプトをプリロードすることです:

const Rifle = preload("res://player/weapons/Rifle.gd")
var my_rifle: Rifle

2番目の方法は、作成時に class_name キーワードを使用することです。上記の例では、Rifle.gdは次のようになります:

extends Node2D
class_name Rifle

class_name を使用する場合、GodotはRifle型をグローバルにエディタに登録します。定数にプリロードすることなく、どこでも使用できます:

var my_rifle: Rifle

変数のキャスト

型キャストは型付き言語の重要な概念です。キャストは、ある型から別の型への値の変換です。

extends Area2D であなたのゲームに敵がいると仮定してください。あなたはそれを PlayerController と呼ばれるスクリプトが付いている KinematicBody2D と衝突させたいのです。衝突を検出するには on_body_entered シグナルを使用します。型付きコードでは、 _on_body_entered コールバックでは、検出したボディは一般的な PhysicsBody2D になり、 PlayerController にはなりません。

この PhysicsBody2D がプレイヤーであるかどうかを確認するには、 as キャストキーワードを使用し、再度コロン : を使用して変数にこの型を使用させます。これにより、変数は PlayerController タイプに固定されます:

func _on_body_entered(body: PhysicsBody2D) -> void:
    var player := body as PlayerController
    if not player:
        return

    player.damage()

As we're dealing with a custom type, if the body doesn't extend PlayerController, the playervariable will be set to null. We can use this to check if the body is the player or not. We will also get full autocompletion on the player variable thanks to that cast.

注釈

組み込み型でキャストしようとして失敗すると、Godotはエラーをスローします。

セーフ・ライン

You can also use casting to ensure safe lines. Safe lines are a new tool in Godot 3.1 to tell you when ambiguous lines of code are type-safe. As you can mix and match typed and dynamic code, at times, Godot doesn't have enough information to know if an instruction will trigger an error or not at runtime.

This happens when you get a child node. Let's take a timer for example: with dynamic code, you can get the node with $Timer. GDScript supports duck-typing, so even if your timer is of type Timer, it is also a Node and an Object, two classes it extends. With dynamic GDScript, you also don't care about the node's type as long as it has the methods you need to call.

ノードを取得したときに期待される型をGodotに伝えるためにキャストを使用できます: ($Timer as Timer)($Player as KinematicBody2D) など。Godotは、そのタイプが機能することを確認し、機能する場合は、行番号がスクリプトエディタの左側で緑に変わります。

Unsafe vs Safe Line

Unsafe line (line 7) vs Safe Lines (line 6 and 8)

注釈

セーフ・ラインをオフにするか、エディタ設定でカラーを変更することができます。

アロー(->)による関数の戻り値の定義

関数の戻り値の型を定義するには、宣言の後に -> を記述し、その後に戻り値の型を記述します:

func _process(delta: float) -> void:
    pass

void 型は、関数が何も返さないことを意味します。変数と同様に、任意の型を使用できます:

func hit(damage: float) -> bool:
    health_points -= damage
    return health_points <= 0

独自のノードを戻り値の型として使用することもできます:

# Inventory.gd

# Adds an item to the inventory and returns it.
func add(reference: Item, amount: int) -> Item:
    var item: Item = find_item(reference)
    if not item:
        item = ItemDatabase.get_instance(reference)

    item.amount += amount
    return item

静的と動的: どちらかのみを利用する

Typed GDScript and dynamic GDScript can coexist in the same project. But I recommended to stick to either style for consistency in your codebase, and for your peers. It's easier for everyone to work together if you follow the same guidelines, and faster to read and understand other people's code.

Typed code takes a little more writing, but you get the benefits we discussed above. Here's an example of the same, empty script, in a dynamic style:

extends Node


func _ready():
    pass


func _process(delta):
    pass

そして静的型付けでは:

extends Node


func _ready() -> void:
    pass


func _process(delta: float) -> void:
    pass

As you can see, you can also use types with the engine's virtual methods. Signal callbacks, like any methods, can also use types. Here's a body_entered signal in a dynamic style:

func _on_Area2D_body_entered(body):
    pass

同じコールバックをタイプヒントとともに使用します:

func _on_area_entered(area: CollisionObject2D) -> void:
    pass

You're free to replace, e.g. the CollisionObject2D, with your own type, to cast parameters automatically:

func _on_area_entered(bullet: Bullet) -> void:
    if not bullet:
        return

    take_damage(bullet.damage)

The bullet variable could hold any CollisionObject2D here, but we make sure it is our Bullet, a node we created for our project. If it's anything else, like an Area2D, or any node that doesn't extend Bullet, the bullet variable will be null.

警告システム

注釈

Documentation about the GDScript warning system has been moved to GDScript warning system.

Cases where you can't specify types

To wrap up this introduction, let's cover a few cases where you can't use type hints. All the examples below will trigger errors.

You can't use Enums as types:

enum MoveDirection {UP, DOWN, LEFT, RIGHT}
var current_direction: MoveDirection

You can't specify the type of individual members in an array. This will give you an error:

var enemies: Array = [$Goblin: Enemy, $Zombie: Enemy]

You can't force the assignment of types in a for loop, as each element the for keyword loops over already has a different type. So you cannot write:

var names = ["John", "Marta", "Samantha", "Jimmy"]
for name: String in names:
    pass

Two scripts can't depend on each other in a cyclic fashion:

# Player.gd

extends Area2D
class_name Player


var rifle: Rifle
# Rifle.gd

extends Area2D
class_name Rifle


var player: Player

概要

型付きGDScriptは強力なツールです。Godotのバージョン3.1から利用できるようになり、より構造化されたコードの記述、一般的なエラーの回避、スケーラブルシステムの作成に役立ちます。将来的には、コンパイラーの最適化によって静的型のパフォーマンスも大幅に向上するでしょう。