昨日は設定関係を完成させた。
今日はスタート画面からゲーム画面へ遷移できるようにしていく。
ゲーム画面への遷移
ゲーム画面へ遷移できるようにするために、スタート画面をPlayButtonNodeObserverプロトコルに準拠するようにする。
//============================== // YWF //------------------------------ // StartScene.swift //============================== import SpriteKit import Security public class StartScene: SKScene, PlayButtonNodeObserver { // 省略 public func playButtonIsSelected(button: PlayButtonNode) { let config = Config.getInstance() let humanStatus: PieceNode.Status let computerStatus: Board.Status switch config.firstMove { case .Random: var buf = UnsafeMutablePointer<UInt8>.alloc(1) SecRandomCopyBytes(kSecRandomDefault, 1, buf) let randomValue = buf.memory if randomValue % 2 == 0 { humanStatus = .Bad computerStatus = .Good } else { humanStatus = .Good computerStatus = .Bad } case .Human: humanStatus = .Bad computerStatus = .Good case .Computer: humanStatus = .Good computerStatus = .Bad } let computer: Player let computerName: String switch config.computerLevel { case .Easy: computer = RandomCom() computerName = "Computer - Easy" case .Normal: computer = AlphaBetaCom(status: computerStatus, depth: 3) computerName = "Computer - Normal" case .Hard: computer = AlphaBetaCom(status: computerStatus, depth: 5) computerName = "Computer - Hard" } let scene = GameScene(size: self.size, userName: "You", userStatus: humanStatus, opponentName: computerName, opponentPlayer: computer) let transition = SKTransition.fadeWithDuration(NSTimeInterval(1.0)) self.view?.presentScene(scene, transition: transition) } // 省略 }
Securityをインポートしているのは、乱数を得るため。
このあたりについては、変種オセロをSwiftに移植してみた。(その10) - いものやま。を参照。
やっているのは、設定オブジェクトから設定を取り出して、それにしたがってゲーム画面を作成して表示させるということ。
このとき、トランジションも指定するようにしてある。
オブザーバの解放
さて、画面の遷移自体は上のコードでオシマイなんだけど、このときにちょっと気をつけないといけないのが、メモリリークが発生しないようにすること。
SwiftではObjective-Cと同様に参照カウンタ方式を使っているので、参照がループしてしまっていると、メモリが解放されなくなってしまう。
これまでのコードで、静的に参照がループしているところはないのだけど、一つ例外となるのが、オブザーバ。
各ボタンからオブザーバへの参照で、参照のループが出来てしまう場合がある。(そして、実際それでループが出来ている)
そこで、SceneがViewから取り除かれるときに、各オブザーバへの参照をボタンから取り除いてやる必要がある。
まず、各ボタンのオブザーバを解放する処理。
//============================== // YWF //------------------------------ // LabelButtonNode.swift //============================== import SpriteKit public class LabelButtonNode: SKLabelNode { // 省略 public func removeAllObservers() { self.observers.removeAll() } // 省略 }
//============================== // YWF //------------------------------ // PlayButtonNode.swift //============================== import SpriteKit public class PlayButtonNode: SKSpriteNode { // 省略 public func removeAllObservers() { self.observers.removeAll() } // 省略 }
そして、ConfigNodeの各ボタンからオブザーバを解放する処理。
//============================== // YWF //------------------------------ // ConfigNode.swift //============================== import SpriteKit public class ConfigNode: SKSpriteNode, LabelButtonNodeObserver { // 省略 private var subjectLabelButtonNodes: [LabelButtonNode] public init() { // 省略 self.subjectLabelButtonNodes = [LabelButtonNode]() let texture = ConfigNode.textures["ConfigBack"]! super.init(texture: texture, color: SKColor.whiteColor(), size: texture.size()) /* Labels */ for setting in ConfigNode.labelButtonNodeSettings { let label = LabelButtonNode(name: setting.label, fontSize: setting.fontSize) label.position.x = setting.x label.position.y = setting.y label.horizontalAlignmentMode = setting.align if setting.computerLevel != nil { self.subjectLabelButtonNodes.append(label) label.addObserver(self) self.computerLevel[label] = setting.computerLevel! } else if setting.firstMove != nil { self.subjectLabelButtonNodes.append(label) label.addObserver(self) self.firstMove[label] = setting.firstMove! } else { label.userInteractionEnabled = false } addChild(label) } // 省略 } // 省略 public func removeFromSubjectLabelNodes() { for labelButtonNode in self.subjectLabelButtonNodes { labelButtonNode.removeObserver(self) } } // 省略 }
変種オセロのスタート画面を作ってみた。(その4) - いものやま。で言及したとおり、処理を設定に置き換えて、処理のコードをシンプルにしてあるので、何箇所も修正をする必要がなくなってる。
あとは、これらを呼び出せるように、StartSceneを修正する。
//============================== // YWF //------------------------------ // StartScene.swift //============================== import SpriteKit import Security public class StartScene: SKScene, PlayButtonNodeObserver { // 省略 private var configNode: ConfigNode! private var playButton: PlayButtonNode! private var ruleButton: LabelButtonNode! public override init(size: CGSize) { // 省略 // 各ノードへの参照をプロパティに保持するように修正 } // 省略 public override func willMoveFromView(view: SKView) { self.releaseObservers() } private func releaseObservers() { self.configNode.removeFromSubjectLabelNodes() self.playButton.removeAllObservers() self.ruleButton.removeAllObservers() } }
これでスタート画面からゲーム画面への遷移はOK。
明日は逆に、ゲーム画面からスタート画面への遷移を実装していく。
今日はここまで!