静的型付けGDScript

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

  • GDScriptで型を使用する方法

  • 静的型はバグの回避に役立ちます

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

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

注釈

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

静的型付けの概要

型付きのGDScriptを使うことで、Godotはさらに多くのエラーを、あなたの書いたコードから検出することができます。メソッドを呼び出すと引数の型が表示されるため、あなたとチームメイトは作業中により多くの情報が得られます。

あなたが在庫管理システムをプログラミングしていると想像してください。 Item ノード、次に Inventory をコーディングします。アイテムをインベントリに追加するには、あなたのコードを操作する人々は常に ItemInventory.add メソッドに渡すべきだとします。型を設定すれば、これを強制できます:

# 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ではコードを記述する際に警告が表示されます: 実行時に問題を引き起こす可能性のあるコードのセクションが識別されますが、コードをそのままにしておくかどうかを決めることができます。これについては後で説明します。

静的型を使用すると、コード補完も改善されます。以下に、 PlayerController というクラスを例に動的な型と静的な型の補完の違いを示します。

あなたはおそらく以前に変数にノードを格納していて、オートコンプリートの提案なしでピリオドが残るようにタイプしました:

code completion options for dynamic

これは動的コードによるものです。Godotは、関数に渡されているノードまたは値の型を認識できません。ただし型を明示的に記述すると、すべてのpublicメソッドと変数がノードから取得されます:

code completion options for typed

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

概して、型付きプログラミングはより構造化された経験をあなたに与えます。それはエラーを防ぎ、あなたのスクリプトを自己文書化の面で改善するのを助けます。これはチームや長期的なプロジェクトで作業している場合に特に役立ちます:調査によると他の人が書いたコードを読んだり、過去に自分が書いたスクリプトの内容を忘れている事に、開発者が大半の時間を費やしています。コードが明確で構造化されているほど、速く理解ができ、速く前に進むことができます。

静的型付けの使用方法

変数または定数の型を定義するには、変数の名前の後にコロンを書き、その後に指定する型を書きます。例: var health: int。 これにより、変数の型は常に保持されます:

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

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

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

現在、使えるのは3つのタイプの… 型 (type) です:

  1. 組み込み型

  2. コアクラスとノード ( ObjectNodeArea2DCamera2D など)

  3. Your own custom classes. Look at the new class_name feature to register types in the editor.

注釈

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

カスタム変数型

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

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

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

class_name Rifle
extends Node2D

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()

ここではカスタムされた型を扱っているため、 bodyPlayerController を継承しない場合、 player 変数は null に設定されます。これを使って、bodyがplayerかどうかを確認できます。このキャストのおかげで、player変数の完全なオートコンプリートもできるようになります。

注釈

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

セーフ・ライン

セーフ・ラインを確保するためにキャストを使用することもできます。セーフ・ラインは、はGodot 3.1の新しいツールで、あいまいなコード行(ライン)が安全(セーフ)な型であることを教えてくれます。型指定されたコードと動的なコードを混在させると、Godotは実行時に命令がエラーを引き起こすかどうかを知るための十分な情報が得られない場合があります。

これは子ノードを取得したときに起こります。たとえばタイマーを考えてみましょう。動的コードでは、$Timer でノードを取得できます。GDScriptは ダック・タイピング をサポートしているので、あなたのタイマーが Timer 型であっても、それは Node であり Object でもあり、その2つのクラスを継承しています。動的GDScriptでは、ノードが呼び出す必要のあるメソッドを持っている場合、その型については気にする必要はありません。

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

Unsafe vs Safe Line

セーフ・ラインではない行(line 7)とセーフ・ライン (line 6 とline 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

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

型付きGDScriptと動的GDScriptは、同じプロジェクト内に共存できます。しかし、コードベースの一貫性のためにも、仲間のためにも、どちらかスタイルに統一することをお勧めします。同じガイドラインに従うことで全員が協力しやすくなり、他の人が書いたコードを素早く読んで理解できるようになります。

型付けされたコードはもう少し記述が必要ですが、上で説明したような利点があります。次に、同じ空のスクリプトを例にあげます。動的なスタイルでは:

extends Node


func _ready():
    pass


func _process(delta):
    pass

そして静的型付けでは:

extends Node


func _ready() -> void:
    pass


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

このように、エンジンの仮想メソッドで型を使用することもできます。シグナルのコールバックも、他のメソッドと同様に、型を使用できます。動的スタイルの body_entered シグナルは次のとおりです:

func _on_Area2D_body_entered(body):
    pass

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

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

パラメーターを自動的にキャストするには、例えば``CollisionObject2D`` などを独自の型に自由に置き換えます:

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

    take_damage(bullet.damage)

bullet 変数にはここで任意の CollisionObject2D を保持できますが、それが独自に作成したノードである Bullet であることを確認します。それ以外の、 Area2D のような Bullet を継承していないノードの場合、 bullet 変数は null になります。

警告システム

注釈

GDScriptの警告システムについてのドキュメントは GDScript警告システム に移動しました。

型の指定ができない場合

この紹介の締めくくりに、タイプヒントを使用できないいくつかのケースを取り上げましょう。 以下の例はすべてエラーを引き起こすでしょう

Enumを型として使うことは出来ません:

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

配列の個々のメンバの型を指定することはできません。これにより、エラーが発生します:

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

for キーワード・ループの各要素にはすでに異なる型があるため、 for ループ内で型の割り当てを強制することはできません。したがって、次のように書くことは**できません**:

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

2つのスクリプトは、循環するようにに相互に依存することはできません:

# Player.gd

extends Area2D
class_name Player


var rifle: Rifle
# Rifle.gd

extends Area2D
class_name Rifle


var player: Player

概要

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