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

いものやま。

雑多な知識の寄せ集め

変種オセロのスタート画面を作ってみた。(その3)

ゲーム開発 Swift YWF

昨日はボタン類の実装をした。

今日は設定の描画を実装していく。

設定の描画

ということで、さっそくコードを。

//==============================
// YWF
//------------------------------
// ConfigNode.swift
//==============================

import SpriteKit

public class ConfigNode: SKSpriteNode, LabelButtonNodeObserver {
  private static let labelFontSize: CGFloat = 56.0
  private static let selectTextFontSize: CGFloat = 48.0
  
  private static let labelPositionX: [CGFloat] = [-163.0, 163.0]
  private static let labelPositionY: [CGFloat] = [210.0, 150.0]
  
  private static let selectTokenPositionX: [CGFloat] = [-285.0, 45.0]
  private static let selectTokenPositionY: [CGFloat] = [50.0, -45.0, -140.0]
  private static let selectTextPositionX: [CGFloat] = [-240.0, 90.0]
  private static let selectTextPositionY: [CGFloat] = [30.0, -65.0, -160.0]
  
  private static var textures = [String: SKTexture]()
  
  public class func loadAssets() {
    let atlas = SKTextureAtlas(named: "Config")
    let names = ["ConfigBack", "BadSelectToken", "GoodSelectToken"]
    for name in names {
      ConfigNode.textures[name] = atlas.textureNamed(name)
    }
  }
  
  private var computerLevel: String
  private var firstMove: String
  
  private var computerLevelSelectToken: SKSpriteNode!
  private var firstMoveSelectToken: SKSpriteNode!
  
  public init() {
    self.computerLevel = ""
    self.firstMove = ""
    self.computerLevelSelectToken = nil
    self.firstMoveSelectToken = nil
    
    let texture = ConfigNode.textures["ConfigBack"]!
    super.init(texture: texture, color: SKColor.whiteColor(), size: texture.size())
    
    /* Computer Level Label */
    let labelComputer = LabelButtonNode(name: "Computer", fontSize: ConfigNode.labelFontSize)
    labelComputer.userInteractionEnabled = false
    labelComputer.position.x = ConfigNode.labelPositionX[0]
    labelComputer.position.y = ConfigNode.labelPositionY[0]
    addChild(labelComputer)
    let labelLevel = LabelButtonNode(name: "Level", fontSize: ConfigNode.labelFontSize)
    labelLevel.userInteractionEnabled = false
    labelLevel.position.x = ConfigNode.labelPositionX[0]
    labelLevel.position.y = ConfigNode.labelPositionY[1]
    addChild(labelLevel)
    
    /* Computer Level Select List */
    let selectTextEasy = LabelButtonNode(name: "Easy", fontSize: ConfigNode.selectTextFontSize)
    selectTextEasy.horizontalAlignmentMode = .Left
    selectTextEasy.position.x = ConfigNode.selectTextPositionX[0]
    selectTextEasy.position.y = ConfigNode.selectTextPositionY[0]
    selectTextEasy.addObserver(self)
    addChild(selectTextEasy)
    let selectTextNormal = LabelButtonNode(name: "Normal", fontSize: ConfigNode.selectTextFontSize)
    selectTextNormal.horizontalAlignmentMode = .Left
    selectTextNormal.position.x = ConfigNode.selectTextPositionX[0]
    selectTextNormal.position.y = ConfigNode.selectTextPositionY[1]
    selectTextNormal.addObserver(self)
    addChild(selectTextNormal)
    let selectTextHard = LabelButtonNode(name: "Hard", fontSize: ConfigNode.selectTextFontSize)
    selectTextHard.horizontalAlignmentMode = .Left
    selectTextHard.position.x = ConfigNode.selectTextPositionX[0]
    selectTextHard.position.y = ConfigNode.selectTextPositionY[2]
    selectTextHard.addObserver(self)
    addChild(selectTextHard)
    
    /* First Move Label */
    let labelFirst = LabelButtonNode(name: "First", fontSize: ConfigNode.labelFontSize)
    labelFirst.userInteractionEnabled = false
    labelFirst.position.x = ConfigNode.labelPositionX[1]
    labelFirst.position.y = ConfigNode.labelPositionY[0]
    addChild(labelFirst)
    let labelMove = LabelButtonNode(name: "Move", fontSize: ConfigNode.labelFontSize)
    labelMove.userInteractionEnabled = false
    labelMove.position.x = ConfigNode.labelPositionX[1]
    labelMove.position.y = ConfigNode.labelPositionY[1]
    addChild(labelMove)
    
    /* First Move Select List */
    let selectTextRandom = LabelButtonNode(name: "Random", fontSize: ConfigNode.selectTextFontSize)
    selectTextRandom.horizontalAlignmentMode = .Left
    selectTextRandom.position.x = ConfigNode.selectTextPositionX[1]
    selectTextRandom.position.y = ConfigNode.selectTextPositionY[0]
    selectTextRandom.addObserver(self)
    addChild(selectTextRandom)
    let selectTextHuman = LabelButtonNode(name: "Human", fontSize: ConfigNode.selectTextFontSize)
    selectTextHuman.horizontalAlignmentMode = .Left
    selectTextHuman.position.x = ConfigNode.selectTextPositionX[1]
    selectTextHuman.position.y = ConfigNode.selectTextPositionY[1]
    selectTextHuman.addObserver(self)
    addChild(selectTextHuman)
    let selectTextComputer = LabelButtonNode(name: "Computer", fontSize: ConfigNode.selectTextFontSize)
    selectTextComputer.horizontalAlignmentMode = .Left
    selectTextComputer.position.x = ConfigNode.selectTextPositionX[1]
    selectTextComputer.position.y = ConfigNode.selectTextPositionY[2]
    selectTextComputer.addObserver(self)
    addChild(selectTextComputer)
    
    /* Computer Level Select Token */
    self.computerLevel = "Easy"
    self.computerLevelSelectToken = SKSpriteNode(texture: ConfigNode.textures["BadSelectToken"])
    self.computerLevelSelectToken.position.x = ConfigNode.selectTokenPositionX[0]
    self.computerLevelSelectToken.position.y = ConfigNode.selectTokenPositionY[0]
    addChild(self.computerLevelSelectToken)
    
    /* First Move Select Token */
    self.firstMove = "Random"
    self.firstMoveSelectToken = SKSpriteNode(texture: ConfigNode.textures["GoodSelectToken"])
    self.firstMoveSelectToken.position.x = ConfigNode.selectTokenPositionX[1]
    self.firstMoveSelectToken.position.y = ConfigNode.selectTokenPositionY[0]
    addChild(self.firstMoveSelectToken)
  }

  public required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
  
  public func labelButtonIsSelected(button: LabelButtonNode) {
    switch button.text {
    case "Easy":
      if self.computerLevel != "Easy" {
        self.computerLevel = button.text
        let fadeOut = SKAction.fadeOutWithDuration(NSTimeInterval(0.2))
        self.computerLevelSelectToken.runAction(fadeOut) {
          self.computerLevelSelectToken.position.y = ConfigNode.selectTokenPositionY[0]
          let fadeIn = SKAction.fadeInWithDuration(NSTimeInterval(0.2))
          self.computerLevelSelectToken.runAction(fadeIn)
        }
      }
    case "Normal":
      if self.computerLevel != "Normal" {
        self.computerLevel = button.text
        let fadeOut = SKAction.fadeOutWithDuration(NSTimeInterval(0.2))
        self.computerLevelSelectToken.runAction(fadeOut) {
          self.computerLevelSelectToken.position.y = ConfigNode.selectTokenPositionY[1]
          let fadeIn = SKAction.fadeInWithDuration(NSTimeInterval(0.2))
          self.computerLevelSelectToken.runAction(fadeIn)
        }
      }
    case "Hard":
      if self.computerLevel != "Hard" {
        self.computerLevel = button.text
        let fadeOut = SKAction.fadeOutWithDuration(NSTimeInterval(0.2))
        self.computerLevelSelectToken.runAction(fadeOut) {
          self.computerLevelSelectToken.position.y = ConfigNode.selectTokenPositionY[2]
          let fadeIn = SKAction.fadeInWithDuration(NSTimeInterval(0.2))
          self.computerLevelSelectToken.runAction(fadeIn)
        }
      }
    case "Random":
      if self.firstMove != "Random" {
        self.firstMove = button.text
        let fadeOut = SKAction.fadeOutWithDuration(NSTimeInterval(0.2))
        self.firstMoveSelectToken.runAction(fadeOut) {
          self.firstMoveSelectToken.position.y = ConfigNode.selectTokenPositionY[0]
          let fadeIn = SKAction.fadeInWithDuration(NSTimeInterval(0.2))
          self.firstMoveSelectToken.runAction(fadeIn)
        }
      }
    case "Human":
      if self.firstMove != "Human" {
        self.firstMove = button.text
        let fadeOut = SKAction.fadeOutWithDuration(NSTimeInterval(0.2))
        self.firstMoveSelectToken.runAction(fadeOut) {
          self.firstMoveSelectToken.position.y = ConfigNode.selectTokenPositionY[1]
          let fadeIn = SKAction.fadeInWithDuration(NSTimeInterval(0.2))
          self.firstMoveSelectToken.runAction(fadeIn)
        }
      }
    case "Computer":
      if self.firstMove != "Computer" {
        self.firstMove = button.text
        let fadeOut = SKAction.fadeOutWithDuration(NSTimeInterval(0.2))
        self.firstMoveSelectToken.runAction(fadeOut) {
          self.firstMoveSelectToken.position.y = ConfigNode.selectTokenPositionY[2]
          let fadeIn = SKAction.fadeInWithDuration(NSTimeInterval(0.2))
          self.firstMoveSelectToken.runAction(fadeIn)
        }
      }
    default:
      break
    }
  }
}

いやー、我ながら酷いコードだ(^^;

まぁ、設定のモデルオブジェクトも作らないといけないし、リファクタリングはそのときにでも。
このコードがどれくらいキレイになるのかというのも、面白いだろうし。

スタート画面の修正

前回、プレイボタン(PlayButtonnode)とラベルボタン(LabelButtonNode)も作ったので、今回の設定の描画(ConfigNode)とあわせてスタート画面(StartScene)の修正もやっておく。

//==============================
// YWF
//------------------------------
// StartScene.swift
//==============================

import SpriteKit

public class StartScene: SKScene {
  private static let MaxWidth: CGFloat = 828.0
  private static let MinMargin: CGFloat = 20.0
  
  private static let TitleImageSize = CGSize(width: 760.0, height: 255.0)
  private static let ConfigImageSize = CGSize(width: 760.0, height: 760.0)
  private static let PlayButtonImageSize = CGSize(width: 200.0, height: 100.0)
  private static let RuleButtonSize = CGSize(width: 120.0, height: 56.0)
  
  private var scale: CGFloat
  private var margin: CGFloat
  
  public override init(size: CGSize) {
    self.scale = 1.0
    self.margin = 0.0
    
    super.init(size: size)
    
    let background = SKSpriteNode(imageNamed: "Background")
    background.position.x = size.width / 2.0
    background.position.y = size.height / 2.0
    self.addChild(background)
    
    self.settingWithSize(size)
    
    let title = SKSpriteNode(imageNamed: "Title")
    title.xScale = self.scale
    title.yScale = self.scale
    
    let config = ConfigNode()
    config.xScale = self.scale
    config.yScale = self.scale
    
    let playButton = PlayButtonNode()
    playButton.xScale = self.scale
    playButton.yScale = self.scale
    
    let ruleButton = LabelButtonNode(name: "Rule", fontSize: 56.0)
    ruleButton.xScale = self.scale
    ruleButton.yScale = self.scale
    ruleButton.verticalAlignmentMode = .Center
    
    let centerX = size.width / 2.0
    title.position.x = centerX
    title.position.y = (size.height - self.margin
                        - StartScene.TitleImageSize.height * self.scale / 2.0)
    config.position.x = centerX
    config.position.y = (size.height - self.margin * 2.0
                         - StartScene.TitleImageSize.height * self.scale
                         - StartScene.ConfigImageSize.height * self.scale / 2.0)
    playButton.position.x = centerX
    playButton.position.y = (size.height - self.margin * 3.0
                             - StartScene.TitleImageSize.height * self.scale
                             - StartScene.ConfigImageSize.height * self.scale
                             - StartScene.PlayButtonImageSize.height * self.scale / 2.0)
    ruleButton.position.x = centerX
    ruleButton.position.y = (size.height - self.margin * 4.0
                             - StartScene.TitleImageSize.height * self.scale
                             - StartScene.ConfigImageSize.height * self.scale
                             - StartScene.PlayButtonImageSize.height * self.scale
                             - StartScene.RuleButtonSize.height * self.scale / 2.0)
    
    self.addChild(title)
    self.addChild(config)
    self.addChild(playButton)
    self.addChild(ruleButton)
  }

  public required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
  
  private func settingWithSize(size: CGSize) {
    self.scale = size.width / StartScene.MaxWidth
    
    let titleHeight = StartScene.TitleImageSize.height * self.scale
    let configHeight = StartScene.ConfigImageSize.height * self.scale
    let playButtonHeight = StartScene.PlayButtonImageSize.height * self.scale
    let ruleButtonHeight = StartScene.RuleButtonSize.height * self.scale
    
    self.margin = (size.height - titleHeight - configHeight - playButtonHeight - ruleButtonHeight) / 5.0
    
    if self.margin < StartScene.MinMargin {
      self.margin = StartScene.MinMargin
      let bodyHeight = size.height - self.margin * 5.0
      let contentHeight = (StartScene.TitleImageSize.height
                           + StartScene.ConfigImageSize.height
                           + StartScene.PlayButtonImageSize.height
                           + StartScene.RuleButtonSize.height)
      self.scale = bodyHeight / contentHeight
    }
  }
}

変更したのは、画像をただ置いていただけのところを実際に働くオブジェクトに置き換えたこと。
あと、ちょっとした整形とか。

動作確認

ここまでで動作確認をすると、次のような感じ。

設定項目をタッチすると、選択されている項目が変更されている(ように見える)ことが分かると思う。
シーン切り替えの実装をしていないので、プレイボタンやルール表示ボタンを押しても、まだ何も起こらないけど。

今日はここまで!