読者です 読者をやめる 読者になる 読者になる

いものやま。

雑多な知識の寄せ集め

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

昨日の続き。

今日はボードの描画を実装していく。

ボードの描画

ボードの描画は、SKSpriteNodeを継承したBoardNodeを作って実装する。

//==============================
// YWF
//------------------------------
// BoardNode.swift
//==============================

import SpriteKit

public class BoardNode: SKSpriteNode {
  // 続く

トークンの位置

まず、トークンの位置をクラス定数として用意しておく。

  // 続き

  private static let tokenPosition = [
    Board.Status.Bad: CGPoint(x: SquareNode.size.width * CGFloat(-5),
                              y: SquareNode.size.height * CGFloat(-4)),
    Board.Status.Common: CGPoint(x: SquareNode.size.width * CGFloat(-5),
                                 y: 0.0),
    Board.Status.Good: CGPoint(x: SquareNode.size.width * CGFloat(-5),
                               y: SquareNode.size.height * CGFloat(4)),
  ]

  // 続く

なお、以前作ったBoardクラスでは、マスの情報を表すのにBoard.Colorという列挙型を使っていたけど、もはや黒/灰色/白というのではなく、悪い子/普通の子/良い子という表現の方がいいので、Board.Statusという列挙型に変えて、列挙子も.Bad/.Common/.Goodに変えている。

画像のロード

駒と同様に、ボードの画像をロードするクラスメソッドを用意する。

  // 続き
  
  private static var textures = [String: SKTexture]()

  public class func loadAssets() {
    let atlas = SKTextureAtlas(named: "Board")
    let names = ["BoardBase", "BoardOver", "Token"]
    for name in names {
      BoardNode.textures[name] = atlas.textureNamed(name)
    }
  }

特筆すべきことは、特にないかな?
Board.atlasというTextureAtlasを用意して、それを読み込み、texturesというクラス変数に格納しているだけ。

プロパティとイニシャライザ

次はプロパティとイニシャライザ。

  // 続き

  private var board: Board
  
  private var squares: [[SquareNode!]]
  private var token: SKSpriteNode!

  public init(board: Board) {
    self.board = board
    self.squares = [
      [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
      [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
      [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
      [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
      [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
      [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
      [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
      [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
      [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
      [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
    ]
    self.token = nil

    let baseTexture = BoardNode.textures["BoardBase"]!
    super.init(texture: baseTexture,
               color: SKColor.whiteColor(),
               size: baseTexture.size())

    for row in Board.RowMin...Board.RowMax {
      for col in Board.ColMin...Board.ColMax {
        self.squares[row][col] = SquareNode(row: row, col: col)
        self.squares[row][col].position.x = SquareNode.size.width * CGFloat(col - 5)
        self.squares[row][col].position.y = SquareNode.size.height * CGFloat(5 - row)
        self.addChild(self.squares[row][col])
      }
    }
    
    let overTexture = BoardNode.textures["BoardOver"]!
    let over = SKSpriteNode(texture: overTexture)
    self.addChild(over)
    
    self.token = SKSpriteNode(texture: BoardNode.textures["Token"])
    self.token.position = BoardNode.tokenPosition[.Common]!
    self.addChild(token)
    
    for row in Board.RowMin...Board.RowMax {
      for col in Board.ColMin...Board.ColMax {
        switch board.status(row, col) {
        case .Bad:
          self.squares[row][col].addPiece(.Bad)
        case .Common:
          self.squares[row][col].addPiece(.Common)
        case .Good:
          self.squares[row][col].addPiece(.Good)
        default:
          break
        }
      }
    }
    let tokenPosition = BoardNode.tokenPosition[board.token]!
    self.token.position = tokenPosition
    
    self.userInteractionEnabled = true
  }

  public required init?(coder aDecoder: NSCoder) {
      fatalError("init(coder:) has not been implemented")
  }

  // 続く

プロパティとしては、モデルとなるボードと、マスのインスタンスを入れる二次元配列、それとトークンを持っている。

そして、イニシャライザでは、モデルとなるボードを受け取って、それにしたがって対応するマスに駒を配置したり、トークンの位置を変えたりしている。

タッチされたマスの検出

最後に、タッチされたマスの検出の実装。

  // 続き
  
  public override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
    let touch = touches.first as! UITouch
    let location = touch.locationInNode(self)
    loop: for node in self.nodesAtPoint(location) {
      switch node {
      case let squareNode as SquareNode:
        println("row: \(squareNode.row), col: \(squareNode.col)")
        break loop
      default:
        break
      }
    }
  }
}

とりあえずはタッチされたマスの場所をコンソールに出力するだけ。

ポイントは、タッチされた場所にあるノードの集まりからSquareNodeを探しているところ。
SquareNodeが見つかれば、その情報を見ることで、タッチされたマスの場所を簡単に知ることが出来る。

ボードの描画の動作確認

ボードの描画の動作確認をするために、コントローラのコードをちょろっと書き換える。

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

import UIKit
import SpriteKit

class GameViewController: UIViewController {
  @IBOutlet weak var skView: SKView!
  
  override func viewDidLoad() {
    BoardNode.loadAssets()
    PieceNode.loadAssetsAndCreateTemplates()
  }
  
  override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    
    var size = self.view.bounds.size
    if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
      size.height *= 2
      size.width *= 2
    }
  
    let scene = GameScene(size: size)
    scene.scaleMode = .AspectFill
    
    let backgroundTexture = SKTexture(imageNamed: "Background")
    let background = SKSpriteNode(texture: backgroundTexture)
    background.position = CGPoint(x: scene.frame.width/2, y: scene.frame.height/2)
    scene.addChild(background)
    
    var board = Board()
    board = board.play(6, 7)
    board = board.play(7, 6)
    board = board.change(6, 6)
    board = board.play(5, 7)
    
    let boardNode = BoardNode(board: board)
    background.addChild(boardNode)
    
    self.skView.presentScene(scene)
  }
  
  // hide status bar.
  override func prefersStatusBarHidden() -> Bool {
    return true
  }
}

やっているのは、モデルとなるボードのインスタンスを用意して、そこからBoardNodeを作り、表示させているだけ。

これを実行すると、以下のような表示がされる。

iPhone 4S f:id:yamaimo0625:20150725091328p:plain

iPhone 6 Plus f:id:yamaimo0625:20150725091342p:plain

マスをタッチすると、タッチされたマスの情報もコンソールに表示される。

今日はここまで!