iPhoneゲームをSwift言語で作成してみたいという初心者向けにiOSのゲームフレームワークを使った作り方を一から解説する入門連載。今回は、GoFデザインパターンの1つである「Stateパターン」やiOS 9から登場した「GKStateMachine」クラスを取り入れることで、今後の開発に役立つ知見を共有します。
本連載「iOS SDKとSwiftで始めるゲーム作成入門」は、iPhone向けのゲーム開発の入門連載です。タワーディフェンスを題材に、「SpriteKit」というゲーム開発フレームワークの解説やゲームの開発手法について書いています。
実装に入る前に本連載で作るアプリの完成形を確認しておきます。本連載では、下記6つのルールを満たすタワーディフェンスを作っていきます。
前回の『デザインパターン「ファーストクラスコレクション」でSwiftコードの保守性・可読性を上げる方法をゲームのコードから学ぶ』では、敵が何体も登場して、全てを倒すとゲームクリアになるように実装しました。
今回の記事では自機の動きを改善していきます。それと平行してGoFデザインパターンの1つである「Stateパターン」やiOS 9から登場した「GKStateMachine」クラスを取り入れることで、今後の開発に役立つ知見を共有できればと思います。
今は自機と敵が接触したときに敵が消滅する仕様になっています。それを実際のタワーディフェンスのように敵が自機近くに来ると攻撃をするように改善しようと思います。
ソースコードは、こちらをベースに改善していきます。
まずはGameScene.swiftを修正して自機が敵の進路方向に入らないようにします。
class GameScene: SKScene, SKPhysicsContactDelegate { // 略... // char.position = CGPoint(x:250, y:300) char.position = CGPoint(x:270, y:280) // 略... }
次にEnemyList.swiftへ自機付近の敵一覧を取得するメソッドを追加します。
class EnemyList { // 略... func enemiesCloseToPoint(point: CGPoint, distance: Double) -> [SKSpriteNode] { return enemies.filter { let dx = Double(point.x - $0.frame.origin.x) let dy = Double(point.y - $0.frame.origin.y) let distanceFromEnemy = sqrt(dx*dx + dy*dy) return distanceFromEnemy < distance } } // 略... }
最後にGameScene.swiftに自機付近の敵を攻撃する処理を追加します。近くの敵を攻撃して敵の削除をする処理と、全ての敵が消えたらゲームクリアにする処理を追加します。
class GameScene: SKScene, SKPhysicsContactDelegate { // 略... override func update(currentTime: NSTimeInterval) { enemyList.enemiesCloseToPoint(char.frame.origin, distance: 50).forEach { $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) } } } // 略... }
これで攻撃範囲に入った敵を攻撃するようになりました。
今まで使っていた、自機と敵が衝突したときの処理は不要なので削除します。
// これらの処理を丸ごと削除する // func didBeginContact(contact: SKPhysicsContact) { // [contact.bodyA, contact.bodyB].forEach { // if $0.node?.name == "enemy" { // $0.node?.removeFromParent() // $0.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) // } // }
Copyright © ITmedia, Inc. All Rights Reserved.