デザインパターン「ファーストクラスコレクション」でSwiftコードの保守性・可読性を上げる方法をゲームのコードから学ぶ:iOS SDKとSwiftで始めるゲーム作成入門(4)(2/2 ページ)
iPhoneゲームをSwift言語で作成してみたいという初心者向けにiOSのゲームフレームワークを使った作り方を一から解説する入門連載。今回は、今後発生し得る要望を意識したクラス設計のために、デザインパターン「ファーストクラスコレクション」を適用する方法を解説。
敵を複数体出現させる
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一覧を扱う処理の分割
最後にコードを少しだけ読みやすくしましょう。先ほどはフィールドの生成処理を分割しましたが、今回は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を使う
今回はコードの可読性・拡張性を意識しながら機能の実装を進めました。本記事で紹介した生成処理の分割やファーストクラスコレクションの利用はゲーム以外の開発でも活用できると思うので、ぜひ試してみてください。
次回は、味方の自由な設置や強化処理を作っていきます。iOS 9で加わったGameplayKitのStateMachineを利用し、Stateパターンを使って味方の攻撃可能状態・攻撃不可状態の処理を分離する方法を学びましょう。
筆者紹介
杉本裕樹
田町のベンチャーで働くエンジニア。
仕事ではiPhoneアプリの開発やRailsを使ったWebサービス開発を行っている。最近のマイブームはUnityを使った3Dゲーム開発。
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- 開発者向け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の基礎―変数、定数、型、演算
本連載では、これからプログラミングやiPhoneアプリ開発を始めてみたい方を対象に、開発に必要な知識を基礎から解説していきます。今回は、Swiftの変数、定数、型、演算などについてサンプルプログラムを交えて解説します。 - iOSアプリ開発でObjective-CからSwiftに移行するための手順、注意点まとめ〜言語仕様の違いは? 連携時の呼び出し方は?
開発生産性や品質を向上させたいiOSアプリ開発者のためにObjective-CからSwiftへ移行するメリットや手順、注意点など勘所をまとめて紹介します。 - いまなら無料! Unityで始めるiPhoneゲーム作成“超”入門
3Dコンテンツ開発ツール「Unity」を使った、簡単な3Dミニゲーム制作の流れを一から解説していきます