いものやま。

雑多な知識の寄せ集め

「BirdHead」のUIを作ってみた。(その2)

昨日は画面構成とモデル/コントローラ/ビューの関係について説明した。

今日はカードの表示を作っていく。

CardNode

カードはSKSpriteNodeを継承したノードとして実装した。

//==============================
// BirdHead
//------------------------------
// CardNode.swift
//==============================

import SpriteKit

class CardNode: SKSpriteNode {
  // 続く

画像のロード

カードのノードで使う画像をロードしたりとか。

  // 続き

  private static var faceDownTexture: SKTexture! = nil
  private static var faceUpTextures = [Int: SKTexture]()
  
  private static var disabledFaceDownTexture: SKTexture! = nil
  private static var disabledFaceUpTextures = [Int: SKTexture]()
  
  private(set) static var size: CGSize! = nil
  
  class func loadAssets() {
    let textureAtlas = SKTextureAtlas(named: "Card")
    CardNode.faceDownTexture = textureAtlas.textureNamed("CardBack")
    CardNode.disabledFaceDownTexture = textureAtlas.textureNamed("CardBackDisabled")
    for i in Deck.MinCard...Deck.MaxCard {
      let textureName = String(format: "Card%02d", i)
      CardNode.faceUpTextures[i] = textureAtlas.textureNamed(textureName)
      let disabledTextureName = String(format: "Card%02dDisabled", i)
      CardNode.disabledFaceUpTextures[i] = textureAtlas.textureNamed(disabledTextureName)
    }
    
    let dummyCard = try! CardNode.get(2, withFaceUp: false)
    CardNode.size = dummyCard.size
  }

  // 続く

画像はテクスチャー・アトラスとして用意してある。
(テクスチャー・アトラスについては、SpriteKitのサンプルコードを読んでみた。(その2) - いものやま。を参照)

なお、手番プレイヤー以外の手札は、暗めに表示したいので、その暗めの状態を「無効状態」として、それ用の画像も用意した。

それと、カードのサイズを参照することがけっこうあるので、ダミーのカードを作ってそのサイズを保持し、参照できるようにしてある。

カードの取得

次はカードの取得。

  // 続き
  
  class func get(card: Int, withFaceUp: Bool) throws -> CardNode {
    guard (Deck.MinCard <= card) && (card <= Deck.MaxCard) else {
      throw Deck.Error.OutOfRange
    }
    
    let cardTexture: SKTexture
    if withFaceUp {
      cardTexture = CardNode.faceUpTextures[card]!
    } else {
      cardTexture = CardNode.faceDownTexture
    }
    
    return CardNode(card: card, faceUp: withFaceUp, texture: cardTexture)
  }

  // 続く

これはクラスメソッドとして、範囲外のカードを取得しようとした場合、例外を投げるようにしてある。

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

続いてプロパティとイニシャライザ。

  // 続き
  
  let card: Int
  private(set) var isFaceUp: Bool
  var isDisabled: Bool {
    didSet {
      if self.isDisabled {
        if self.isFaceUp {
          self.texture = CardNode.disabledFaceUpTextures[self.card]!
        } else {
          self.texture = CardNode.disabledFaceDownTexture
        }
      } else {
        if self.isFaceUp {
          self.texture = CardNode.faceUpTextures[self.card]!
        } else {
          self.texture = CardNode.faceDownTexture
        }
      }
    }
  }
  
  private init(card: Int, faceUp: Bool, texture: SKTexture) {
    self.card = card
    self.isFaceUp = faceUp
    self.isDisabled = false
    
    super.init(texture: texture,
               color: SKColor.clearColor(),
               size: texture.size())
  }

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

  // 続く

プロパティは、カードの数字、表向きかどうか、無効状態かどうか。

無効状態かどうかにはプロパティオブザーバを追加して、状態が変更されたら画像も変更されるようにした。

カードの表裏の変更

最後にカードの表裏の変更。

  // 続き
  
  func faceUp() {
    if !self.isFaceUp {
      if self.isDisabled {
        self.texture = CardNode.disabledFaceUpTextures[self.card]!
      } else {
        self.texture = CardNode.faceUpTextures[self.card]!
      }
      self.isFaceUp = true
    }
  }
  
  func faceDown() {
    if self.isFaceUp {
      if self.isDisabled {
        self.texture = CardNode.disabledFaceDownTexture
      } else {
        self.texture = CardNode.faceDownTexture
      }
      self.isFaceUp = false
    }
  }
}

これもプロパティオブザーバで実装してもよかったのだけど、「カードをめくる」という動作が現実にあるので、メソッドとした方が直感的だと思い、メソッドにした。

今日はここまで!