GameSceneクラスがすっきりしたところで機能を追加します。以下のようにGameSceneに敵が複数体出現する処理を追加します。
class GameScene: SKScene, SKPhysicsContactDelegate {
    enum State {
        case Playing
        case GameClear
        case GameOver
    }
    var state = State.Playing
    var enemies = [SKSpriteNode]()
    let char = SKSpriteNode(imageNamed: "Char")
    var routes = [float2]()
 
    override func didMoveToView(view: SKView) {
        // 省略
 
        routes = routesWithField(field)
 
        // 敵を10体出現させる処理
        (0...10).forEach {
            performSelector("createEnemy", withObject: nil, afterDelay: Double($0))
        }
    }
 
    func createEnemy() {
        guard let view = view else {
            return
        }
 
        let enemy = SKSpriteNode(imageNamed: "Enemy")
        var routes = self.routes
        var prevPosition = routes.removeFirst()
        let actions = routes.map { p -> SKAction in
            let dx = p.x - prevPosition.x
            let dy = p.y - prevPosition.y
            let duration = Double(sqrt(dx * dx + dy * dy) / 100)
            prevPosition = p
            return SKAction.moveTo(CGPoint(x: Double(p.x), y: Double(p.y)), duration: duration)
        }
 
        let fieldImageLength = view.frame.width / 10
        enemy.name = "enemy"
        enemy.position = CGPoint(x: fieldImageLength * 2, y: view.frame.height)
        enemy.physicsBody = SKPhysicsBody(rectangleOfSize: enemy.size)
        enemy.runAction(SKAction.sequence(actions)) {
            self.state = .GameOver
 
            let myLabel = SKLabelNode(fontNamed: "HiraginoSans-W6")
            myLabel.text = "ゲームオーバー"
            myLabel.fontSize = 45
            myLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame) - 20)
            self.addChild(myLabel)
        }
        addChild(enemy)
 
        enemies.append(enemy)
    }
 
    func didBeginContact(contact: SKPhysicsContact) {
        [contact.bodyA, contact.bodyB].forEach {
            if $0.node?.name == "enemy" {
                $0.node?.removeFromParent()
                $0.node?.removeAllActions()
            }
        }
 
        if enemies.filter({ $0.parent != nil }).count == 0 {
            state = .GameClear
 
            let myLabel = SKLabelNode(fontNamed: "HiraginoSans-W6")
            myLabel.text = "ゲームクリア"
            myLabel.fontSize = 45
            myLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame) - 20)
            addChild(myLabel)
        }
    }
 
    // 省略
}
今回はdidMoveToViewメソッド内で行っていたEnemyの生成を「createEnemy」メソッド(24〜56行目)に移動しました。そのcreateEnemyメソッドを1秒ごとに呼ぶことで敵を複数体出現させています。
1秒ごとに呼び出す処理にはperformSelectorメソッドを使っています(20行目)。このメソッドはメソッドの遅延実行ができるメソッドで引数のafterDelayを調整することで遅延時間を変更できます。
最後にコードを少しだけ読みやすくしましょう。先ほどはフィールドの生成処理を分割しましたが、今回はEnemyの一覧の管理を分割します。「EnemyList」というクラスを作って以下のように修正します。
import UIKit
import SpriteKit
 
class EnemyList {
    private var enemies = [SKSpriteNode]()
 
    func appendEnemy(enemy: SKSpriteNode) {
        enemies.append(enemy)
    }
 
    func isAllEnemyRemoved() -> Bool {
        return enemies.filter { $0.parent != nil }.count == 0
    }
}
次にGameSceneを以下のように3カ所書き換えます。
class GameScene: SKScene, SKPhysicsContactDelegate {
    // 省略
    // var enemies = [SKSpriteNode]()
    var enemyList = EnemyList()
    // 省略
 
    func createEnemy() {
        // 省略
        // enemies.append(enemy)
        enemyList.appendEnemy(enemy)
    }
 
    func didBeginContact(contact: SKPhysicsContact) {
        // 省略
        // if enemies.filter({ $0.parent != nil }).count == 0 {
        if enemyList.isAllEnemyRemoved() {
            // 省略
        }
    }
    // 省略
}
EnemyListように配列を扱うクラスを「ファーストクラスコレクション」と呼びます。今回のように配列を扱うクラスを作っておけば、配列に関する処理をこのクラスに集約できます。
今回のケースでは、ファーストクラスコレクションにする恩恵はそこまで大きくありませんが、今後「特定条件で全てのEnemyの速度を倍にする」「プレイヤーが特定アイテムを使った時に全てのEnemyを一時停止させたい」などの要望が出てきたときに恩恵を感じることができるかと思います。
今回はコードの可読性・拡張性を意識しながら機能の実装を進めました。本記事で紹介した生成処理の分割やファーストクラスコレクションの利用はゲーム以外の開発でも活用できると思うので、ぜひ試してみてください。
次回は、味方の自由な設置や強化処理を作っていきます。iOS 9で加わったGameplayKitのStateMachineを利用し、Stateパターンを使って味方の攻撃可能状態・攻撃不可状態の処理を分離する方法を学びましょう。
田町のベンチャーで働くエンジニア。
仕事ではiPhoneアプリの開発やRailsを使ったWebサービス開発を行っている。最近のマイブームはUnityを使った3Dゲーム開発。
 開発者向けiOS 9、WatchOS 2、Swift 2、Xcode 7の新機能と新しいApple Developer Programの参考情報まとめ
開発者向けiOS 9、WatchOS 2、Swift 2、Xcode 7の新機能と新しいApple Developer Programの参考情報まとめ Apple WatchやiPhoneのアプリを作ろう! Playgroundで学ぶSwiftの基礎―変数、定数、型、演算
Apple WatchやiPhoneのアプリを作ろう! Playgroundで学ぶSwiftの基礎―変数、定数、型、演算 iOSアプリ開発でObjective-CからSwiftに移行するための手順、注意点まとめ〜言語仕様の違いは? 連携時の呼び出し方は?
iOSアプリ開発でObjective-CからSwiftに移行するための手順、注意点まとめ〜言語仕様の違いは? 連携時の呼び出し方は? いまなら無料! Unityで始めるiPhoneゲーム作成“超”入門
いまなら無料! Unityで始めるiPhoneゲーム作成“超”入門Copyright © ITmedia, Inc. All Rights Reserved.