昨日は手札のタッチ処理を実装した。
今日はそれを使って人間プレイヤーを実装し、コントローラ(シーン)に修正を加えて、実際に遊べるようにする。
Human
さっそく、人間プレイヤーの実装。
//============================== // BirdHead //------------------------------ // Human.swift //============================== import Foundation class Human: Player, PlayableHandNodeDelegate { // 続く
PlayerプロトコルとPlayableHandNodeDelegateプロトコルに準拠するようにしてある。
プロパティとイニシャライザ
まずはプロパティとイニシャライザ。
// 続き private(set) var name: String private(set) var isCom: Bool private let handNode: PlayableHandNode private var currentView: GameInfo.PlayerView! private let selectQueue: NSOperationQueue private var selectedAction: Action! init(name: String, handNode: PlayableHandNode) { self.name = name self.isCom = false self.handNode = handNode self.currentView = nil self.selectQueue = NSOperationQueue() self.selectQueue.maxConcurrentOperationCount = 1 self.selectedAction = nil } // 続く
特に難しいことはなく。
一つ、説明が必要なところとして、selectQueueというオペレーションキューを用意しているということ。
これは入力待ちを行うためのもので、変種オセロのUIを作ってみた。(その15) - いものやま。を参照。
(ただし、今回はNSOperationQueue#suspendプロパティを使って、実行待ちを行うようにしている)
アクションの選択
次に、アクションの選択。
// 続き func select(view: GameInfo.PlayerView) throws -> Action { self.selectQueue.suspended = true let semaphore = NSBlockOperation { /* do nothing */ } self.selectQueue.addOperation(semaphore) self.currentView = view self.handNode.playableHandNodeDelegate = self self.handNode.setLegalActions(view.legalActions) semaphore.waitUntilFinished() self.handNode.removeLegalActions() self.handNode.playableHandNodeDelegate = nil self.currentView = nil if self.selectedAction == nil { throw PlayerError.SelectIsCanceled } let selectedAction = self.selectedAction self.selectedAction = nil return selectedAction } // 続く
最初にselectQueueのsuspendedプロパティをtrueにしておいて、入力待ち出来るようにしておく。
そして、セマフォの役割を果たすオペレーションをキューに追加。
そしたら、PlayableHandNodeの委譲先を自分に設定して、PlayableHandNodeに合法手をセットし、入力待ちへ。
入力があって待ち状態が解除されたら、後片付けをしたあとに入力の内容を受け取り、選択されたアクションを返している。
PlayableHandNodeDelegateプロパティへの準拠
最後に、PlayableHandNodeからの通知への対応。
// 続き func playableHandNodeSelectCardNodes(cardNodes: [CardNode]) { var cards = [Int]() for cardNode in cardNodes { cards.append(cardNode.card) } let playAction = Action.play(cards) let discardAction = Action.discard(cards) let legalActions = self.currentView.legalActions if legalActions.contains(playAction) { self.selectedAction = playAction } else if legalActions.contains(discardAction) { self.selectedAction = discardAction } self.selectQueue.suspended = false } }
まず、選択されたカードの情報から選択された合法手を得る。
そのあとは、selectQueueのsuspendedプロパティをfalseにすることで、入力待ちを解除する。
これで人間プレイヤーの実装は完了。
GameSceneの修正
人間プレイヤーの実装が出来たので、それに合わせてコントローラ(シーン)の修正を行う。
//============================== // BirdHead //------------------------------ // GameScene.swift //============================== import SpriteKit class GameScene: SKScene, GameInfoObserver, ButtonNodeDelegate { // 省略 init(size: CGSize, info: GameInfo) { self.info = info let handNode0 = PlayableHandNode(frameWidth: size.width) let human = Human(name: "You", handNode: handNode0) let sarsaParameterURL = NSBundle.mainBundle().URLForResource("SarsaComParameter", withExtension: "plist")! self.players = [ human, SarsaCom.load(sarsaParameterURL), SarsaCom.load(sarsaParameterURL), SarsaCom.load(sarsaParameterURL), ] // 以下、省略
まず、イニシャライザで、SarsaAIの代わりに人間プレイヤーを作るようにする。
このとき、PlayableHandNodeが必要となるので、人間プレイヤーの手札だけは先に作っておくようにする。
基本的にはこれだけでOKなんだけど、手番が来たときに、全部のカードを明るくしてしまうんじゃなくて、プレイできるカードだけ明るくしたほうがいいので、次のような変更を入れておく。
- self.handNodes[turnPlayerIndex].enableAll() + let playerView = try! self.info.playerViewFor(turnPlayerIndex) + if let playableHandNode = self.handNodes[turnPlayerIndex] as? PlayableHandNode { + playableHandNode.setLegalActions(playerView.legalActions) + } else { + self.handNodes[turnPlayerIndex].enableAll() + }
なお、同様の箇所が何個かあるけど、全部同じように変更する。
これでコントローラの修正もOK。
動作確認
さっそく動作確認。
ちゃんとプレイ出来てることが分かると思う。
今日はここまで!