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

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

f:id:tukumosanzou:20180710010513j:plain

今回は以前作成しました、func createPlayer()にレーザー砲を追加したいと思います。

ここで発射音に使うパラメーターを追加しますが、ついでに物理ボディが衝突した時の爆発音とゲームのBGMの各パラメーターも追加したいと思います。



まず効果音に使うサウンドファイルを追加します。

SpriteKitはサウンドファイルを簡単に追加する方法があります。


struct PhysicsCategories {} の直前に以下のコードを追加します。

//BGM用のパラメーターを作成。
var backgroundMusic = SKAudioNode()
//BGMで流れるゲーム音楽。
let musicURL = Bundle.main.url(forResource: "music", withExtension: "m4a")

//laserを発射するときの効果音。    
let laserSound = SKAction.playSoundFileNamed("LaserSoundEffect.mp3", waitForCompletion: false)
    
//playerやenemyが爆発したときの効果音。
let explosionSound = SKAction.playSoundFileNamed("explosion.wav", waitForCompletion: false)

Budle.main.url(forResource: "ファイル名" withEtension: "ファイルの拡張子") で文字列でプロジェクトにバンドルされたファイルを探します。

playSoundNamed("ファイル名.拡張子", waitCompletion: bool値) で waitCompletion がtrueの場合、このアクションの継続時間はオーディオ再生の長さと同じです。

falseの場合、アクションはすぐに完了したものとみなされます。


BGMを追加するのは簡単です。

didMove() 内にbadkgroundMusic = SKAudioNode(url: musicUrl) で音源をセットし addChild(backgroundMusic) でGameScene に追加するだけです。

didMove()の終了直前に追加します。

url(forResource:withExtention:) - Bundle | Apple Developer Documentation
SKAudioNode - SpriteKit | Apple Developer Documentation
playSoundFileNamed(_:waitForCompletion) - SKAction | Apple Developer Documentation



全体図はこうなります。

import SpriteKit

import GameplayKit

class GameScene: SKScene, SKPhysicsContactDelegate {
    
        //省略
    
    //BGM用のパラメーターを作成。
    var backgroundMusic = SKAudioNode()
    //BGMで流れるゲーム音楽。
    let musicURL = Bundle.main.url(forResource: "music", withExtension: "m4a")

    //laserを発射するときの効果音。    
    let laserSound = SKAction.playSoundFileNamed("LaserSoundEffect.mp3", waitForCompletion: false)
    
    //playerやenemyが爆発したときの効果音。
    let explosionSound = SKAction.playSoundFileNamed("explosion.wav", waitForCompletion: false)

    struct PhysicsCategories {
       //省略
    }

    override func didMove(to view: SKView) {

        //省略

        //BGM用の音楽をセットする
        backgroundMusic = SKAudioNode(url: musicURL!)
        //GameSceneに追加する。
        addChild(backgroundMusic)
    }

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

        //最初のタッチがあったら以降の処理を実行します、なければ処理を抜けます。
        guard let touch = touches.first else { return }
        
        //laserを作成する関数を呼び出す。      
        createLaser()
            
        
    }

    createEnemy(){
        //省略
    }

    func createLaser() {

        //SpriteNodeを作成する
        let laser = SKSpriteNode(imageNamed: "laser")
        
        //物理ボディを設定。
        laser.physicsBody = SKPhysicsBody(circleOfRadius: max(laser.size.width / 2, laser.size.height / 2))

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

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

        //物理ボディにコンタクトテストマスクを設定する。
        laser.physicsBody!.contactTestBitMask = PhysicsCategories.enemy
        
        //重なり順番を5に設定する。
        laser.zPosition = 5

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

        //ポジションをplayerのすぐ上にする。
        laser.position = CGPoint(x: player.position.x, y: player.position.y + player.size.height * 0.8)

        //GameSceneに追加する。
        addChild(laser)
        
        // 1秒間隔でlaserの高さ分Y軸方向に移動する。
        let moveLaser = SKAction.moveTo(y: self.size.height + laser.size.height, duration: 1)

        //GameSceneから削除。
        let moveReset = SKAction.removeFromParent()

        //順番にアクションを実行するsequenceを作る。
        let moveSequence = SKAction.sequence([laserSound, moveLaser, moveReset])
        
        //アクショッンを実行する。
        laser.run(moveSequence)
    }

    createPlayer() {
        //省略
    }

    createBackground() {
        //省略
    }

}



createLaser() は createEnemy() とほぼ同じです。

SpriteKitはこのように同じようなコードが出てきますので慣れてきたらリファクタリングで整理すると良いと思います。

違う部分といえば、今まではdidMove()内部に関数の呼び出しを追加してましたが、タッチするたびにレーザーがplayerから発射されるようにしたいので、override func touchesBegan() によって createLaser() が呼び出されるようにします。


以下のようになっている部分です。

//省略

//タッチするたびに呼び出される処理。
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

        //最初のタッチがあったら以降の処理を実行します、なければ処理を抜けます。
        guard let touch = touches.first else { return }
        
        //laserを作成する関数を呼び出す。      
        createLaser()
            
}

//省略



touches.firstとは最初のタッチのことです、それを検出したら以降の処理を行うようにしています。

gurad 条件 else { return }は条件が満たされない場合はreturnで処理を抜けます。
SpriteKit(swift)プログラミングではよく見られる手法です。

touchesBegan(_:with) - UIResponder
Statements — The Swift Programming Language (Swift 4.2)



ここでシミュレーターで確認すると、画面をタッチしたらレーザーが発射されると思います。



今回は以上です、また次回。