【GameplayKit】iOSゲームアプリにおけるStateパターン実践 Part_3


前回までの記事

【GameplayKit】iOSゲームアプリにおけるStateパターン実践 Part_2では、給水機が取りうる全ての状態に共通する機能が実装されたDispenserStateクラスを定義しました。

この記事について

本記事では、ステートとステートマシンの実装を行います。

給水機が変化しうるそれぞれの状態(ステート)を表現するクラスを定義します。
給水機は以下の状態に変化します。

  • 満タン(FullState)
  • 水あり(PartiallyFullState)
  • カラ (EmptyState)
  • 補給中(RefillingState)
  • 給水中(ServeState)

定義されたステートは、ステートマシンによって管理・操作します。
ステートマシンを使うことで、給水機そのものを操作するよりも効率的に給水機を扱うことができます。

手順

  1. FullStateクラスを定義
  2. ステートマシンを定義

満タン状態を表現するクラスを定義する

メニューバーから「File > New > File... > iOS > Swift File」を選択します。
ファイル名はFullState.swiftにしておきます。
SpriteKitGameplayKitフレームワークをインポートして、DispenserStateクラスを継承したFullStateクラスを定義します。

FullState.swift
import SpriteKit
import GameplayKit

class FullState: DispenserState {

}

親クラスに定義されているイニシャライザを利用して、必須イニシャライザを定義します。
また、didEnter(from:)メソッドとwillExit(to:)メソッドをオーバーライドします。

FullState.swift
class FullState: DispenserState {

    init(game: GameScene) {
        super.init(game: game, associatedNodeName: "FullState")
    }

    override func didEnter(from previousState: GKState?) {
        super.didEnter(from: previousState)

        let green = SKColor.green
        changeIndicatorLightToColor(green)
    }

    override func willExit(to nextState: GKState) {
        super.willExit(to: nextState)

        let black = SKColor.black
        changeIndicatorLightToColor(black)
    }

}

didEnter(from:)メソッドとwillExit(to:)メソッドでは、給水機の表示ランプの色を「満タンになったらグリーン・満タンではなくなったらブラック」に変化させています。

ステートマシンを定義する

ステートマシンは各種状態への遷移を管理する役割を持ち、GKStateMachineクラスによって表現されます。
ゲームシーンに、GKStateMachineクラスのオブジェクトを定義します。

まず、GameScene.swiftファイルにGameplayKitフレームワークをインポートしておきます。
メンバワイズプロパティにstateMachineプロパティを暗黙アンラップなオプショナルで定義します。
stateMachineプロパティは、didMove(to:)メソッドで初期化します。引数には、GKState型オブジェクトの配列を渡します。

GameScene.swift
import SpriteKit
import GameplayKit

class GameScene: SKScene {

    var stateMachine: GKStateMachine!

    override func didMove(to view: SKView) {
        let fullState = FullState(game: self)
        stateMachine = GKStateMachine(states: [fullState])

        stateMachine.enter(FullState.self)
    }

    override func didChangeSize(_ oldSize: CGSize) {
        let dispenser = childNode(withName: "dispenser")
        dispenser?.position.x = size.width / 2
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        let locationInView = touches.first?.location(in: view)
        let locationInScene = convertPoint(fromView: locationInView!)

        let refillButton = childNode(withName: "//refillButton")
        let location = locationInScene

        if atPoint(location) == refillButton {
            print("attempt to refill.")
        } else {
            print("attempt to dispense")
        }
    }
}

didMove(to:)メソッドで、給水機の初期状態がFullStateになるように記述しています。

ビルド

シミュレータでビルドしてみます。
給水機の表示ランプindicatorがグリーンになっていれば、状態がFullStateになっていることになります。また、状態遷移パネルにある「Full」がハイライトされていることも確認しておきましょう。

図. 満タン状態の給水機

図. 満タン状態ではない給水機

次回

ステートマシンを実現するGKStateMachineクラスを使って、状態GKStateオブジェクトを管理することができました。
ステートマシンでは、GKState型のインスタンスを操作せずにクラスそのものを扱っていることに注目できます。
次回【GameplayKit】iOSゲームアプリにおけるStateパターン実践 Part_4では、他の状態オブジェクトを定義して、状態から状態への繊維を実装します。