検索
連載

ゲームのState管理を簡単にするiOS 9 GameplayKitのクラスとはiOS SDKとSwiftで始めるゲーム作成入門(5)(2/4 ページ)

iPhoneゲームをSwift言語で作成してみたいという初心者向けにiOSのゲームフレームワークを使った作り方を一から解説する入門連載。今回は、GoFデザインパターンの1つである「Stateパターン」やiOS 9から登場した「GKStateMachine」クラスを取り入れることで、今後の開発に役立つ知見を共有します。

PC用表示 関連情報
Share
Tweet
LINE
Hatena

敵は1回の攻撃で倒せないようにする

 前項でキャラクターの動きがタワーディフェンスに近づいてきました。次は敵が何回も攻撃しないと倒せないようにします。

 敵にlifeプロパティーを持たせるためにEnemyクラスを作成します。「File」→「New」→「File」からEnemyクラスを作成してください。親クラスにはSKSpriteNodeを指定します。

 Enemyクラスを作成したら下のようにlifeプロパティーを付けてください。lifeはひとまず3に設定します。

import UIKit
import SpriteKit
class Enemy: SKSpriteNode {
    var life = 3
}

 次にEnemyList.swiftでSKSpriteNodeを指定している部分をEnemyに置き換えます。

class EnemyList {
    private var enemies = [Enemy]()
 
    func enemiesCloseToPoint(point: CGPoint, distance: Double) -> [Enemy] {
        // 略...
    }
 
    func appendEnemy(enemy: Enemy) {
        enemies.append(enemy)
    }
    // 略...
}

 同時にGameScene.swiftの敵の初期化処理も書き換えます。今まで「SKSpriteNode(imageNamed: "Enemy")」としていたところを「Enemy(imageNamed: "Enemy")」に置き換えます。

class GameScene: SKScene, SKPhysicsContactDelegate {
    // 略...
    func createEnemy() {
        // 略...
        let enemy = Enemy(imageNamed: "Enemy")
        // 略...
    }
    // 略...
}

 最後にGameScene.swiftのupdateメソッドも修正します。今は自機が敵を攻撃した瞬間に敵が消滅しているので、それを敵ライフが0になったら敵が消えるように変えます。

class GameScene: SKScene, SKPhysicsContactDelegate {
    // 略...
    override func update(currentTime: NSTimeInterval) {
        enemyList.enemiesCloseToPoint(char.frame.origin, distance: 50).forEach {
        $0.life -= 1    
        if $0.life <= 0 {
            $0.physicsBody?.node?.removeFromParent()
            $0.physicsBody?.node?.removeAllActions()
 
            if enemyList.isAllEnemyRemoved() {
                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)
                }
            }
        }
    }
    // 略...
}

自機は一定の間隔を空けて攻撃させる

 前項の修正で敵のlifeを設定してきました。敵は3回まで攻撃に耐えれるようになったのですが、今は自機がupdateメソッドが呼ばれるたびに攻撃するので、一瞬で敵が倒されてしまいます。そこで自機が一定の間隔を空けて攻撃するよう修正してゲーム性を高めようと思います。

 具体的な方法として、自機に攻撃可能状態・待機状態の2種類の状態を作ります。そして1度攻撃したら待機状態になり、一定期間が過ぎたら攻撃可能状態に戻るようにします。

 自機に現在の状態を持たせるために、自機を表すCharクラスを作成します。Enemyを作ったときと同様にChar.swiftを作成して下のように置き換えます。

import UIKit
import SpriteKit
 
class Char: SKSpriteNode {
    enum State {
        case Attack
        case Wait
    }
    var state: State = .Attack
}

 GameScene.swiftの中のcharの初期化処理も少し修正します。

class GameScene: SKScene, SKPhysicsContactDelegate {
// 略...
//    let char = SKSpriteNode(imageNamed: "Char")
let char = Char(imageNamed: "Char")
// 略...
}

 次に攻撃可能状態と待機状態を切り替える処理を実装します。GameScene.swiftのupdateを以下のように書き換えてください。

class GameScene: SKScene, SKPhysicsContactDelegate {
    // 略...
    override func update(currentTime: NSTimeInterval) {
        // stateが.Attackのときだけ攻撃する
        if char.state == .Attack {
            enemyList.enemiesCloseToPoint(char.frame.origin, distance: 50).forEach {
                $0.life -= 1
                if $0.life <= 0 {
                    $0.physicsBody?.node?.removeFromParent()
                    $0.physicsBody?.node?.removeAllActions()
 
                    if enemyList.isAllEnemyRemoved() {
                        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)
                    }
                }
                // 敵に攻撃したらstateを.Waitにする
                char.state = .Wait
                // 0.2秒経過したらstateを.Attackに戻す
                performSelector("changeCharStateToAttack", withObject: nil, afterDelay: 0.2)
            }
        }
    }
 
    func changeCharStateToAttack() {
        char.state = .Attack
    }
    // 略...
}

 以上で一定間隔ごとに攻撃する処理の実装が終わりました。次ページではStateパターンを使って今実装したコードに拡張性を持たせていきます。

Copyright © ITmedia, Inc. All Rights Reserved.

ページトップに戻る