いものやま。

雑多な知識の寄せ集め

変種オセロのUIを作ってみた。(その20)

ユーザ情報の表示も出来た。

あとはゲームの結果を表示したりも必要だけど、かなり長くなったので、とりあえずまとめていく。

ターンコントローラの修正

とりあえず、終了判定の実装だけは入れておく。

//==============================
// YWF
//------------------------------
// TurnController.swift
//==============================

import Foundation

public class TurnController: BoardNodeObserver, ButtonNodeObserver {
  // 省略
  
  public func boardNodeActionIsFinished(node: BoardNode) {
    if self.undoOperation != nil {
      if self.undoOperation.finished {
        let board = self.boardNode.board
        let turnPlayer = (board.turn == .Bad) ? self.badPlayer : self.goodPlayer
        if board.move > 0 && turnPlayer.isCom {
          // turn player is not human,
          // so undo again.
          self.undoOperation = NSBlockOperation {
            self.boardNode.undo()
          }
          self.queue.addOperation(self.undoOperation)
          return
        } else {
          // undo has been finished.
          self.undoOperation = nil
          
          if let lastAction = self.boardNode.lastAction {
            switch lastAction {
            case let .Play(row, col):
              self.boardNode.lightUpSquare(row, col, withOpponent: true)
            case let .Change(row, col):
              self.boardNode.lightUpSquare(row, col, withOpponent: true)
            default:
              break
            }
          }
        }
      } else {
        // do nothing,
        // because undo has been requested and not been finished.
        return
      }
    }
    
    if self.boardNode.board.isGameEnd {
      let winner: Player!
      if self.boardNode.board.win(.Bad) {
        winner = self.badPlayer
      } else if self.boardNode.board.win(.Good) {
        winner = self.goodPlayer
      } else {
        winner = nil
      }
      
      if winner != nil {
        println("game end: \(winner) won.")
      } else {
        println("draw.")
      }
    } else {
      self.selectOperation = NSBlockOperation {
        self.requestAction()
      }
      self.queue.addOperation(self.selectOperation)
    }
  }
  
  // 省略
}

ボードでアクションが終わったところで、ゲームが終了しているか判断している。
そして、ゲームが終了していたら、とりあえずどちらが勝ったかをログに出力して、それ以上アクションの選択がされないようにしている。

ゲームシーンの実装

さて、いままでビューコントローラでベタにやっていたシーンの構築を、ゲームシーンのイニシャライザへ移動する。

//==============================
// YWF
//------------------------------
// GameScene.swift
//==============================

import SpriteKit

public class GameScene: SKScene {
  private var turnController: TurnController!
  
  public init(size: CGSize,
              userName: String, userStatus: PieceNode.Status,
              opponentName: String, opponentPlayer: Player) {
    self.turnController = nil
    
    super.init(size: size)
    
    self.scaleMode = .AspectFill
    
    let layoutManager = LayoutManager.getInstanceFor(size)
    
    let backgroundTexture = SKTexture(imageNamed: "Background")
    let background = SKSpriteNode(texture: backgroundTexture)
    background.position = layoutManager.getPosition(.Background, relativeTo: .Scene)
    self.addChild(background)
    
    let headerPosition = layoutManager.getPosition(.Header, relativeTo: .Background)
    if headerPosition != nil {
      let headerTexture = SKTexture(imageNamed: "Header")
      let header = SKSpriteNode(texture: headerTexture)
      header.position = headerPosition
      background.addChild(header)
      
      let logoTexture = SKTexture(imageNamed: "Logo")
      let logo = SKSpriteNode(texture: logoTexture)
      logo.position = layoutManager.getPosition(.Logo, relativeTo: .Header)
      header.addChild(logo)
    }
    
    var board = Board()
    let boardNode = BoardNode(board: board)
    boardNode.position = layoutManager.getPosition(.Board, relativeTo: .Background)
    background.addChild(boardNode)
    
    let undoButton = ButtonNode(type: .Undo)
    let passButton = ButtonNode(type: .Pass, enabled: false)
    let exitButton = ButtonNode(type: .Exit)
    undoButton.position = layoutManager.getPosition(.UndoButton, relativeTo: .Background)
    passButton.position = layoutManager.getPosition(.PassButton, relativeTo: .Background)
    exitButton.position = layoutManager.getPosition(.ExitButton, relativeTo: .Background)
    background.addChild(undoButton)
    background.addChild(passButton)
    background.addChild(exitButton)
    
    let userInfoSize = layoutManager.getSize(.UserInfo)
    let opponentInfoSize = layoutManager.getSize(.OpponentInfo)
    let opponentStatus: PieceNode.Status = (userStatus == .Bad) ? .Good: .Bad
    let userInfoNode = UserInfoNode(playerName: userName, status: userStatus, size: userInfoSize, boardNode: boardNode)
    let opponentInfoNode = UserInfoNode(playerName: opponentName, status: opponentStatus, size: opponentInfoSize, boardNode: boardNode)
    userInfoNode.position = layoutManager.getPosition(.UserInfo, relativeTo: .Background)
    opponentInfoNode.position = layoutManager.getPosition(.OpponentInfo, relativeTo: .Background)
    background.addChild(userInfoNode)
    background.addChild(opponentInfoNode)
    
    let user = Human(boardNode: boardNode, passButton: passButton)
    let badPlayer: Player
    let goodPlayer: Player
    if userStatus == .Bad {
      badPlayer = user
      goodPlayer = opponentPlayer
    } else {
      badPlayer = opponentPlayer
      goodPlayer = user
    }
    self.turnController = TurnController(boardNode: boardNode, undoButton: undoButton,
                                         badPlayer: badPlayer, goodPlayer: goodPlayer)
  }

  public required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
  
  public override func didMoveToView(view: SKView) {
    self.turnController.start()
  }
}

ただ、ちょっと融通が利かない実装というか、今のところは大丈夫だけど、あとから外部向けのインタフェースが必要になったときに、いろいろ苦しい感じがあるので、あとで修正は必要そう。
(例えば、イニシャライザでやるのは必要なUIの配置だけで、(ファサードクラスのように)外部からシーン内の各要素の設定が出来るようになっていた方が、融通が利く)

なお、Appleのサンプルコードではシーンのロードをバックグラウンドで行うようにしていたけれど( SpriteKitのサンプルコードを読んでみた。(その2) - いものやま。などを参照)、今まで実行していた感じだと、ロードに時間はかかっていなかったので、バックグラウンドで行うようにはしていない。

コントローラの修正

コードをゲームシーンへ移したので、それに合わせてコントローラも修正。

//==============================
// YWF
//------------------------------
// GameViewController.swift
//==============================

import UIKit
import SpriteKit

class GameViewController: UIViewController {
  @IBOutlet weak var skView: SKView!
  
  override func viewDidLoad() {
    BoardNode.loadAssets()
    PieceNode.loadAssetsAndCreateTemplates()
    UserInfoNode.loadAssets()
    ButtonNode.loadAssets()
  }
  
  override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    
    var size = self.view.bounds.size
    if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
      size.width *= 2.0
      size.height *= 2.0
    }
  
    let computer = AlphaBetaCom(status: .Good, depth: 3)
    let scene = GameScene(size: size, userName: "You", userStatus: .Bad, opponentName: "Computer", opponentPlayer: computer)
    
    self.skView.presentScene(scene)
  }
  
  // hide status bar.
  override func prefersStatusBarHidden() -> Bool {
    return true
  }
}

これでUI作りも一段落。

今日はここまで!