Developer

さくさく理解する Godot 入門(ただし2Dに限る)基礎編 シーズン1【第4回】
2021.11.11
Lv1

さくさく理解する Godot 入門(ただし2Dに限る)基礎編 シーズン1【第4回】

目次

衝突判定(KinematicBody2D, StaticBody2D)

物体の自由落下や衝突判定·反射などの物理演算処理を行うには、そのための専用クラスを使用する。
Godot には2D物理演算処理を行うクラスとして以下の4種類のクラスが用意されている

クラス名 概要 基底クラス
Area2D 域への侵入・退出検知。領域内だけの引力を設定することも可能。 CollisionObject2D
StaticBody2D 衝突により移動しない(つまり静的な)物理ボディ PhysicsBody2D
KinematicBody2D 移動・衝突応答をコードで実装する物理ボディ PhysicsBody2D
RigidBody2D 移動・衝突応答を自動的に行ってくれる物理ボディ PhysicsBody2D

物理演算処理クラスそれぞれの概要は概要欄に書いている通りだが、もっとざっくり言うと、地面などの移動しないものはStaticBody2D、プレイヤーが上下左右ボタン等で操作するものはKinematicBody2D、ピンボールのボールのように他のものにぶつかって反射つつ物理法則にしたがって自由落下するものはRigidBody2Dを使用するのが定石だ。

本章では、StaticBody2D、KinematicBody2D を使い、前章と同じ様に画像を表示し、それを上下左右キー移動させるコードを示す。 違いは、前章では画像の移動範囲をスクリプト内での定数で指定したが、本章では画面にノード(オブジェクト)を設置し、 それとの衝突判定により、移動範囲を制限するという点だ。どちらがよい方法なのかは状況により異なるが、 一般的には本章の方法の方が、画面デザインを変更した場合の修正コスト・ミスの発生確率が低く済む点が優れていると考える。 なによりこの方法の方が Godot っぽい気がする。

ルートノードを Node2D とし、KinematicBody2D と KinematicBody2D をその子ノードとして追加する(下図参照)。

KinematicBody2D は画像情報や衝突形状情報を保持していないので、画像(通常は Sprite)ノード、 衝突形状ノード(CollisionShape2D) をその子ノードとして追加する。子ノードは物理ボディクラスが保持するので、いわゆるオブジェクト指向での委譲という形式だ。
何故、物理ボディクラスが画像・衝突判定領域情報を直接保持しないのかと疑問に思われる方もいるかもしれないが、クラスが巨大化するのはメモリ効率やメンテナンス性が低下しよろしくないし、委譲により複数のクラスを使用する方が、柔軟性に優れるからだ。

Sprite ノードを KinematicBody2D の子ノードとしたら、前章同様に画像をアタッチする。
次に、CollisionShape2D も子ノードとする。CollisionShape2D の設定はちょっと手数が多い。
下図のように、インスペクタで「Shape」部分右の▼を押し、ドロップダウンから「新規 RectangleShape2D」を選ぶ。

そうすると、Shape すぐに右に RectangleShape2D と表示されるので、そこをクリックすると下図のようにシェイプのプロパティが表示される。

この状態で Extents の x, y を入力するか、またはスプライトのところに表示された●をドラッグして、矩形シェイプのサイズを設定する(下図参照)。
この例では、矩形形状を選んだが、円やカプセルや任意のポリゴン形状などを選ぶこともできる。

以上で、KinematicBody2D の設定は終わりだ。
同様に StaticBody2D も設置しよう。本稿では、画像ではなく色指定できる矩形の ColorRect を子ノードとして配置している。 CollisionShape2D も矩形と同じ位置になるように設定する。

前章では、各フレームでキーの状態を検知し、画像を移動させる処理を _process(delta) 関数をオーバライドすることで実装したが、 本章では _physics_process(delta) 関数をオーバライドする。
物理ボディをコントロールする場合は、この関数を使用するのが Godot の作法のようだ。

また前章では、Sprite の位置を変更する方法として position プロパティを変更したが、 物理ボディである KinematicBody2D では、position プロパティを直接変更するのではなく、move_and_collide() などの移動関数を使用する。

下図で使用している move_and_collide(Vector2) は、 移動量を引数で受け取り、衝突が発生した場合は、その情報を返す。 本稿では、衝突するとそこで移動を停止するだけだが、スーパーマリオのようにコインを取得するゲームでは、コインと衝突した場合は、 コインを消去し、コイン数を+1するといった処理を行う必要がある。

const MOVE_UNIT = 4
func _physics_process(delta):
    var dx = int(Input.is_action_pressed("ui_right")) - int(Input.is_action_pressed("ui_left"))
    var dy = int(Input.is_action_pressed("ui_down")) - int(Input.is_action_pressed("ui_up"))
    if dx != 0 || dy != 0:
        $KinematicBody2D.move_and_collide(Vector2(dx, dy) * MOVE_UNIT)

以上で、画像が矩形領域と衝突すると、そこで移動が止まるので、移動範囲を制限することができるようになった、というわけだ。

■まとめ

・衝突判定を行うには、物理ボディオブジェクトを使用するといいぞ。
・StaticBody2D は移動しない物理ボディ、KinematicBody2D はコードで動きを制御する物理ボディだぞ。
・StaticBody2D、KinematicBody2D を画面に配置するときは、表示するもの(Sprite, ColorRect など)と、 衝突判定領域(CollisionShape2D)をその子ノードにするんだぞ。
・KinematicBody2D を移動させるときは、position を直接書き換えるのではなく、move_and_collide() 等の専用関数を使うんだぞ。