獣は月夜に何を見る...

SpriteKitでゲーム その1- SPACE SHOOTER③

今回は、playerをつくるfunc createPlayer()を追加したいと思います。
前回作成した、func createBackground()の直前にfunc createPlayer()を追加します、必要なパラメーターもついでに追加します。

import SpriteKit

//SKPhysicsContactDelegateを追加する。
class GameScene: SKScene, SKPhysicsContactDelegate {

    //ゲーム中にplayerを参照する必要があるのでplayerのパラメーターを作成します
    var player: SKSpriteNode!

    //衝突判定で使うビットを構造体で作る。
    struct PhysicsCategories {
        static let none: UInt32 = 0
        static let player: UInt32 = 0x1 << 1 //1
        static let laser: UInt32 = 0x1 << 2 //2
        static let enemy: UInt32 = 0x1 << 3 //3
    }

    override func didMove(to view: SKView) {

       //ゲームに重力シミュレートを追加する
      physicsWorld.gravity = CGVector(dx: 0, dy: 0)

      //物理衝突があったことを通知する。
      physicsWorld.contactDelegate = self

      createBackground()
       
       //関数呼び出し。
      createPlayer()

    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    }

    //playerを作成する
    func createPlayer() {

        //SpriteNodeで使うテクスチャにplayer.pngを指定する。
        let playerTexture = SKTexture(imageNamed: "player")

        //SpriteNodeを作成する
        player = SKSpriteNode(texture: playerTexture)
        
        //衝突判定のために物理ボディを設定する。
        player.physicsBody = SKPhysicsBody(circleOfRadius: max(player.size.width / 2, player.size.height / 2))

        //物理ボディにカテゴリマスクを設定する。
        player.physicsBody!.categoryBitMask = PhysicsCategories.player

        //物理ボディに衝突マスクを設定する。
        player.physicsBody!.collisionBitMask = PhysicsCategories.none

        //物理ボディにコンタクトテストマスクを設定する。
        player.physicsBody!.contactTestBitMask = PhysicsCategories.enemy
        
        //SpriteNodeの大きさを実寸の半分にする。
        player.setScale(0.5)

        //重なり順番を10に設定する。
        player.zPosition = 10

        //名前をつける。
        player.name = "player"

        //最初の出現位置を決める。
        player.position = CGPoint(x: frame.midX, y: UIScreen.main.bounds.height * 0.05)

        //GameSceneに追加する。
        addChild(player)
    }

    func createBackground() {
        //省略
    }
}

func createBackground()と同じ部分は説明を省きます。
大きな違いはphysicsBody(物理エンジン)を追加する部分です。
これを追加することでオブジェクトの衝突判定が可能になり、いろいろと便利になります。
SKPhysicsBody - SpriteKit | Apple Developer Documentation を参照

physicsBody(物理エンジン)を使用しかつ衝突判定がをするためには必ず必要なものがあります、以下の部分です。

//SKPhysicsContactDelegateを追加する。
class GameScene: SKScene, SKPhysicsContactDelegate {
    
    //省略

    override func didMove(to view: SKView) {

        //ゲームに重力シミュレートを追加する
        physicsWorld.gravity = CGVector(dx: 0, dy: 0)

        //物理衝突があったことを通知する。
       physicsWorld.contactDelegate = self

    }

    //省略

}

SKPhysicsContactDelegate--- 物理衝突があったことを知らせるプロトコルです。

physicsWorld.contactDelegate = self --- 知らせる先はどこなのかを指定します、selfなので GameScene になります。

physicsWorld.gravity = CGVector(dx: 0, dy: 0 ) --- 重力のかかる方向を指定します、dx(X軸) dy(Y軸)ですが値が0なのは重力シミュレート自体は必要なのですが影響は受けたくないためです、物理エンジンで衝突判定をするためにはこの指定は必要なのでこのようにしています。

この3つはセットで使用すると思えば良いと思います。

Protocol - The Swift Programming Language (Swift 4.2)
SKPhysicsContactDelegate - SpriteKit | Apple Developer Documentation
SKPhysicsWorld - SpriteKit | Apple Developer Documentation
contactDelegate - Spritekit | Apple Developer Documentation
gravity - SpriteKit | Apple Developer Documentation

衝突判定について少し説明します。

f:id:tukumosanzou:20180705180127p:plain

衝突処理についてですが仮に図のようにplayerとenemyの2つの物体があり衝突するとします、必要なパラメーターは3つです。

categoryBitMask --- 物体を識別する一意の数字です基本的に同じものはありません。

collisionBitMask --- 自分自身に衝突を許可する物体のcategoryBitMaskを指定します複数ある場合は" | "で区切って表記します。

contactTestBitMask --- 設定してある物体にたいして衝突する相手のcategoryBitMaskを指定することで衝突したことにより発生する処理を行いたいときに使います、複数ある場合は" | "で区切って表記します。
ゲームの場合は爆発や得点が入る処理などが主だと思います。

ですが数字で表記すると結局わけがわからなくなりやすいので最初から2進数表現してなおかつ定数・変数に代入してしまうのがわかりやすいようです。

実際のコードをみた方がわかりやすいと思うので、今までの説明を踏まえて。
ここでは先ず、それぞれの物体のcategoryBitMaskを定数に入れて構造体(structures)を作成します。
Structures and Classes - The Swift Programming Language (Swift 4.2)

struct PhysicsCategories {
    static let none: UInt32 = 0
    static let player: UInt32 = 0x1 << 1 //1
    static let laser: UInt32 = 0x1 << 2 //2
    static let enemy: UInt32 = 0x1 << 3 //3
}

図のような部分になります。
struct(構造体)で2進数を各定数に入れます、0x1 << 1 は1のビットシフトです。ビットシフトを使わずplayer = 0b001とういう書き方もありますがその辺りは好みが分かれるところです。
Advanced Operators - The Swift Programming Language (Swift 4.2)

これを使用する場合はPhysicsCategories.playerと表記します。
続いてplayer に物理ボディ(物理エンジン)を追加する部分をです。

func createPlayer() {

    //省略

    //衝突判定のために物理ボディを設定する。
    player.physicsBody = SKPhysicsBody(circleOfRadius: max(player.size.width / 2, player.size.height / 2))

    //物理ボディにカテゴリマスクを設定する。
    player.physicsBody!.categoryBitMask = PhysicsCategories.player

    //物理ボディに衝突マスクを設定する。
    player.physicsBody!.collisionBitMask = PhysicsCategories.none

    //物理ボディにコンタクトテストマスクを設定する。
    player.physicsBody!.contactTestBitMask = PhysicsCategories.enemy

    //省略

}

SKPhysicsBody(circleOfRdius:) --- playerを包み込むように円形の物理ボディを作成します。他にもいくつかパターンがあります。 シミュレータで確認すると player の周りに青い円できてるはずです。

PhysicsCategories.none = 0 なのですがcollisionBitMaskに0を指定すると全ての衝突を受け入れることになります、厳密に指定するのもありなのですがこのゲームの場合playerにはenemyしか衝突しないので1対1の衝突の場合等はそれでも良いと思います。

ちなみにcategoryBitmaskが0は全てに属し、contactTestBitMaskが0は何が衝突しても衝突検出しません。

今回はcontactTestBitMask = PhysicsCategories.enemyとなってますので衝突検出はplayerから見てenemyとだけです。 もし複数の場合は PhysicsCategories.enemy | PhysicsCategories.laser みたいに" | "で区切ります。

SKPhysicsBody - SpriteKit | Apple Developer Documentation
categoryBitMask - SKPhysicsBody | Apple Developer Documentation
collisionBitMask - SKPhysicsBody | Apple Developer Documentation
contactTestBitMask - SKPhysicsBody | Apple Developer Documentation

以上でplayerの作成は終わりましたが、衝突検出をするのに相手側がないとはじまりません。
次回は相手側となるfunc createEnemy()を作成したいと思います。

ではまた次回。



swiftの詳細なら。

オンラインブートキャンプ iPhoneアプリコース