【SpriteKit】衝突イベントを実装してみた

こんにちは、のっくんです。

ゲーム開発の記事も早くも8つ目。

今日は、コリジョンイベント(Collision Event)についてです。

以下のようにアプリを実装してみました。

弾が敵に当たった後に敵が死んでいます。

カエルはその場で透明になり、ハエは落下しています。

この実装はコリジョンイベントを利用することで実現しています。

コリジョンイベントとは、その名の通り、キャラクター同士が衝突・接触した場合のイベントです。

Spritekitでは衝突イベントを管理するために以下の3つのマスクを用意しています。

  • categoryBitMask、自分のカテゴリー。弾と敵。
  • contactTestBitMask、接触した時にイベントを発生させる相手。弾が敵と接触するように設定する。
  • collisionBitMask、衝突する相手。デフォルトは0xFFFFFFFF、すべての物体と衝突するようになっている。

BitMaskを設定する

まずカテゴリーを列挙型で設定します。

enum PhysicsCategory: UInt32{
    case enemy = 1
    case bullet = 2
}

次にShotクラスでは以下のようにbulletカテゴリーを設定、enemyと接触した時にイベントを発生するようにしました。

self.physicsBody?.categoryBitMask = PhysicsCategory.bullet.rawValue
self.physicsBody?.contactTestBitMask = PhysicsCategory.enemy.rawValue

カエルやハエのFrog、Flyクラスでは以下のようにenemyカテゴリーを設定しました。

self.physicsBody?.categoryBitMask = PhysicsCategory.enemy.rawValue

接触時のイベント

カテゴリーが設定できたところで接触時のイベントを実装していきます。

SKPhysicsContactDelegateをGameSceneの中でインプリします。

didBegin(_ contact: SKPhysicsContact) {}が使えるようになるのでこの中で接触イベントを書いていきます。

弾のカテゴリーを取得。

let bulletMask = PhysicsCategory.bullet.rawValue

didBeginの中では、衝突した物体AとBが得られますが、弾がどちらなのかは分かりません。

ですので、Aがだった場合とBが弾だった場合でそれぞれ条件分けします。

// Aが弾だった場合、Bを条件判定対象に
if (contact.bodyA.categoryBitMask & bulletMask) > 0 {
        one = contact.bodyA
        two = contact.bodyB
// Bが弾だった場合、Aを条件判定対象に
}else if(contact.bodyB.categoryBitMask & bulletMask > 0){
        one = contact.bodyB
        two = contact.bodyA
}

これでoneが弾、twoが判定対象として設定されました。

次にtwoが敵だった時の処理を書いていきます。

  // oneは弾
switch two.categoryBitMask {
case PhysicsCategory.enemy.rawValue:
    print("bullet -> enemy")
    
    // 弾を消す
    nodesToRemove.append(one.node!)
    
    // カエルかハエである場合ダウンキャストする
    if let frog = two.node as? Frog{
        frog.die()
    }else if let fly = two.node as? Fly{
        fly.die()
}

弾を消して、敵がカエルもしくはハエだったらそれぞれ死ぬようにしました。

弾を貫通させる

動画をよく見てもらうと分かりますが、死んだカエルは透明になり弾が貫通しています。

着弾直後は透明になって見えますが、存在しないことになっています。

すべての接触や衝突を回避するために、カエルが死ぬ時にはcategoryBitMaskとcollisionBitMaskを0にしています。

// 判定を無効に
self.physicsBody?.collisionBitMask = 0
self.physicsBody?.categoryBitMask = 0

終わりに

いかがでしたでしょうか。

接触や衝突イベントはゲームを作る上で欠かせない要素だと思います。

それでは。