昨日はモデルの保存と復元を行った。
今日は復元されたモデルを元に、ビューを復元する処理を実装する。
HandNodeの修正
まずは手札。
//============================== // BirdHead //------------------------------ // HandNode.swift //============================== import SpriteKit class HandNode: SKNode { // 省略 func restore(cards: [Int]) { for card in cards { let cardNode = try! CardNode.get(card, withFaceUp: self.isFaceUp) cardNode.isDisabled = true self.addChild(cardNode) self.cardNodes.append(cardNode) } var angle = HandNode.angle * CGFloat(self.cardNodes.count - 1) / 2.0 for cardNode in self.cardNodes { let position = CGPoint(x: -self.radius * sin(angle), y: self.radius * cos(angle) - self.baseY) cardNode.zRotation = angle cardNode.position = position angle -= HandNode.angle } } // 省略 } // 省略
手札の情報を受け取って、適切な位置に追加していく。
PlayAreaNodeの修正
次はプレイエリア。
//============================== // BirdHead //------------------------------ // PlayAreaNode.swift //============================== import SpriteKit class PlayAreaNode: SKNode { // 省略 func restore(cards: [Int]) { for card in cards { let cardNode = try! CardNode.get(card, withFaceUp: true) self.addChild(cardNode) self.cardNodes.append(cardNode) } self.cardNodes.sortInPlace { $0.card < $1.card } let width = CardNode.size.width * self.scale * CGFloat(self.cardNodes.count - 1) + PlayAreaNode.margin * CGFloat(self.cardNodes.count - 1) var x = -width / 2.0 for i in 0..<self.cardNodes.count { self.cardNodes[i].position.x = x self.cardNodes[i].zPosition = 0.0 self.cardNodes[i].xScale = self.scale self.cardNodes[i].yScale = self.scale x += CardNode.size.width * self.scale + PlayAreaNode.margin } } // 省略 }
こちらも同じく、プレイされたカードを適切な位置に追加。
MinusPointInfoNodeとMinusCardListNodeの修正
最後にマイナス点一覧の修正。
//============================== // BirdHead //------------------------------ // MinusPointInfoNode.swift //============================== import SpriteKit class MinusPointInfoNode: SKNode { // 省略 func restore(cards: [[Int]]) { for playerIndex in 0..<4 { self.minusCardListNodes[playerIndex].restore(cards[playerIndex]) } } // 省略 }
まずはMinusPointInfoNodeだけど、こちらは各MinusCardListNodeに丸投げ。
//============================== // BirdHead //------------------------------ // MinusCardListNode.swift //============================== import SpriteKit class MinusCardListNode: SKNode { // 省略 func restore(cards: [Int]) { for card in cards { self.point += card let cardNode = try! CardNode.get(card, withFaceUp: true) self.addChild(cardNode) self.cardNodes.append(cardNode) } var x = -self.frameNode.frame.width/2.0 + CardNode.size.width*self.scale/2.0 for cardNode in self.cardNodes { cardNode.position.x = x x += MinusCardListNode.margin + CardNode.size.width*self.scale } self.pointLabelNode.text = String(format: MinusCardListNode.pointFormat, self.point) } // 省略 }
そして、MinusCardListNodeでは、カードを追加し、マイナス点の合計を増やして、点数の表示を更新している。
GameSceneの修正
あとはこれらのメソッドを呼び出して、実際に復元を行うだけ。
コントローラであるGameSceneを修正する。
//============================== // BirdHead //------------------------------ // GameScene.swift //============================== import SpriteKit class GameScene: SKScene, GameInfoObserver, ButtonNodeDelegate { // 省略 init(size: CGSize, info: GameInfo, restore: Bool = false) { // 省略 if restore { self.restoreFromInfo() } } // 省略 private func restoreFromInfo() { for playerIndex in 0..<4 { let playerView = try! self.info.playerViewFor(playerIndex) self.handNodes[playerIndex].restore(playerView.hands) } if self.info.actionsInTrick.count > 0 { var lastPlayIndex = 0 let startPlayerIndex = (self.info.playerCount + self.info.turnPlayerIndex - self.info.actionsInTrick.count) % self.info.playerCount for offset in 0..<self.info.actionsInTrick.count { let playerIndex = (startPlayerIndex + offset) % self.info.playerCount let action = self.info.actionsInTrick[offset] self.playAreaNodes[playerIndex].restore(action.cards()) if case .Play = action { lastPlayIndex = playerIndex } } for playerIndex in 0..<4 { if playerIndex != lastPlayIndex { self.playAreaNodes[playerIndex].disableCardNodes() } } } self.minusPointInfoNode.restore(self.info.minusPointCards) } // 省略 override func didMoveToView(view: SKView) { self.actionQueue.addActionBlock {executor in executor.startAction() self.deckNode.readyToDeal() { self.selectQueue.addOperationWithBlock { if self.info.inDeal { self.selectAndDoAction() } else if !self.info.isEnd { try! self.info.deal() } else { self.actionQueue.addActionBlock { executor in self.minusPointInfoNode.okButtonNode.userInteractionEnabled = false if self.minusPointInfoNode.parent == nil { self.responseLayer.addChild(self.minusPointInfoNode) } executor.startAction() self.minusPointInfoNode.show { executor.endAction() } } self.gameInfoEnded() } } executor.endAction() } } } override func willMoveFromView(view: SKView) { self.info.removeAllObservers() self.infoButtonNode.buttonNodeDelegate = nil self.minusPointInfoNode.okButtonNode.buttonNodeDelegate = nil self.minusPointInfoNode.exitButtonNode.buttonNodeDelegate = nil _ = self.players.map { $0.cancelSelect() } self.selectQueue.cancelAllOperations() self.actionQueue.cancelAllOperations() self.interruptionQueue.cancelAllOperations() self.okButtonNodeCompletions.removeAll() NSNotificationCenter.defaultCenter().postNotificationName(GameInfo.deleteGameInfoKey, object: nil) self.resume() } // 省略 }
GameScene#restoreFromInfo()でビューの状態を復元。
それと、GameScene#didMoveToView(_: SKView)では、現在の状態に応じて、
- ディール中なら、アクションの選択と実行
- ディール外で、ゲームが終了していなければ、ディールを始める
- ディール外で、ゲームが終了していれば、マイナス点一覧を表示して、結果も表示。
としている。
あと、GameScene#willMoveFromView(_: SKView)では、保存しているモデルを削除するようにしている。
ViewController
そういえば、ViewControllerのコードをブログには載せてなかったので、せっかくだから。
//============================== // BirdHead //------------------------------ // ViewController.swift //============================== import UIKit import SpriteKit class ViewController: UIViewController { @IBOutlet weak var skView: SKView! override func viewDidLoad() { super.viewDidLoad() CardNode.loadAssets() MinusPointInfoNode.loadAssets() } override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) var size = self.view.bounds.size if UIDevice.currentDevice().userInterfaceIdiom == .Phone { RuleScene.scale = 2.0 size.width *= 2.0 size.height *= 2.0 } if let gameInfo = GameInfo.fromSaveData() { let gameScene = GameScene(size: size, info: gameInfo, restore: true) self.skView.presentScene(gameScene) } else { let startScene = StartScene(size: size) self.skView.presentScene(startScene) } } override func prefersStatusBarHidden() -> Bool { return true } }
保存されたモデルがあれば、それを使ってゲームを再開。
そうでなければ、スタート画面を表示、という感じ。
今日はここまで!