【Godot】コリジョンレイヤーとマスクについて

この記事では、Godot Engineでの コリジョンレイヤーとマスクについて説明します。

プロジェクト作成時の挙動

プロジェクト作成時、KinematicBody2D などのコリジョンは、すべてのオブジェクトに対して衝突を行います。
これは物理挙動としては正しいのですが、ゲームとしての衝突、特定のオブジェクトに対して衝突を除外するようなときに不便です。

例えば以下のような2Dシューティングの衝突ルールの場合……

  • Playerは、Enemy(敵)とBullet(敵弾)のみ衝突を行いたい
  • Shot(自機から発射される弾)は、Enemyのみ衝突を行いたい
  • Enemyは、PlayerとShotのみ衝突を行いたい
  • Bulletは、Playerとのみ衝突を行いたい
PlayerShotEnemyBullet
Player衝突する衝突する
Shot衝突する
Enemy衝突する衝突する
Bullet衝突する

Godotでは、このように特定のオブジェクトとのみ衝突を行う仕組みとして、「コリジョンレイヤーとマスク」が用意されています。

コリジョンレイヤーの名前を設定

プロジェクト > プロジェクト設定 からプロジェクト設定を開くと、一般 > Layer Names > 2d/3d Physics からレイヤー名を設定できます。(2d/3d Renderは描画用のレイヤーでコリジョンとは別ものとなることに注意!)
今回は2Dで判定を行いたいので、以下のように設定しました。

この設定をすることで、各オブジェクトの PhysicsBody2D > Collision から「コリジョンレイヤーとマスク」をわかりやすく名前で管理することができます。

レイヤーとマスクの関係

レイヤーとマスクの関係は以下のとおりです。

  • レイヤー:自分が所属しているコリジョン
  • マスク:自分が当たり判定を行うコリジョン

例えば Player は Playerレイヤーに所属し、マスク(衝突対象)は Enemy/Bullet となります

Shotは、Shotレイヤーに所属し、マスクはEnemyです

Enemyは、Enemyレイヤーに所属し、マスクはPlayerです

Bulletは、Bulletレイヤーに所属し、マスクはPlayerです

実行して動作を確認してみます。

まずは Player です。
Playerは、EnemyとBulletとのみ当たり判定を行います。

“Shot” とは当たり判定を行わないようになっています。

Shotの当たり判定を見てみます。

Shotは Enemyとのみ当たり判定を行うので、それ以外とは衝突しなくなっています。同じレイヤーである Shot とも衝突しなくなっています。

衝突した対象を判定する

衝突した対象をチェックするには、CollisionObject2D.collision_layer を使用します。

CollisionObject2D とは

CollisionObject2D とは、”Area2D“、”PhysicsBody2D” の基底となるクラスです。”PhysicsBody2D” はサブクラスに “KinematicBody2D”, “RigidBody2D”, “StaticBody2D” が存在します。

move_and_collide() の場合

例えば KinematicBody2D.move_and_collide() で移動した場合には、戻り値は KinematicCollision2D を返します。

# 移動と衝突処理を行う
var collision:KinematicCollision2D = move_and_collide(velocity * delta)
if collision:
  # 衝突した
  var collider:CollisionObject2D = collision.collider # コライダーを取り出す
  if collider.collision_layer & (1 << 1) # layer '2' と衝突した.
    collider.do_something() # 衝突した対象との任意の処理を行うことができる

注意点は以下の2つです。

  • 1. collider.collision_layer はビットなので、ビット演算で判定する必要があります
  • 2. layer番号はエディタの値から “-1” する必要があります

move_and_slide() の場合

KinematicBody2D.move_and_slide() の場合は、複数のコリジョンと衝突する可能性があるので、get_slide_count() を使用する必要があります。

# 移動と衝突処理を行う
velocity = move_and_slide(velocity)
for i in range(get_slide_count()):
  # 1つずつ取り出す.
  var col:ticCollision2D = get_slide_collision(i)
  var collider:CollisionObject2D = collision.collider # コライダーを取り出す
  if collider.collision_layer & (1 << 1) # layer '2' と衝突した.
    collider.do_something() # 衝突した対象との任意の処理を行うことができる

参考