STACKERゲームを作ろう その6

nekoroll.hatenablog.com
の続き

今回やること

  • 下の段と重なっていないマスは点灯しないようにする
  • 1つも重なっていなければゲームオーバー

完成品はこちら
ellie-app.com

解説

下の段と重なっていないマスは点灯しないようにする

止めたPointからy-1して、stoppedLightPointsにあるかどうか見たらOK

update

KeyDown keyType ->
            case keyType of
                Space ->
                    let
                        isStacked { x, y } =
                            if List.isEmpty model.stoppedLightPoints then
                                True

                            else
                                List.any (\p -> p == { x = x + 1, y = y }) model.stoppedLightPoints
                        
                        stackedPoints =
                            List.filter isStacked model.lightPoints
                        
                    in
                    ( { model
                        | stoppedLightPoints = model.stoppedLightPoints ++ stackedPoints
                        , lightPoints = setStartPoints nextRow 3
                      }
                    , Cmd.none
                    )

isStackedで重なっているかどうか判定
最初の行は重なりがないため、停止済みのPointがあるかどうかを判定するようにした
stackedPointsで重なっているPointのみ保存しstoppedLightPointsに保存するようにした

1つも重なっていなければゲームオーバー

  • ゲームの状態を持つようにする
  • 1つも重なっていなければゲームオーバー
  • 重なっているマスがあれば継続
  • ついでにSpaceでリスタートできるようにする

Model

type GameState
    = Playing
    | GameOver

ゲーム進行中とゲームオーバーを状態として持つようにした
これを各パターンマッチに追加していく
コンパイラが全部教えてくれるので凄い楽だった

subscriptions

subscriptions : Model -> Sub Msg
subscriptions model =
    case model.gameState of
        GameOver ->
            Browser.Events.onKeyDown (D.map KeyDown keyDecoder)

        Playing ->
            Sub.batch
                [ Browser.Events.onAnimationFrame Blinking
                , Browser.Events.onKeyDown (D.map KeyDown keyDecoder)
                ]

ゲーム進行中ならボックスの点滅とキーイベントの監視
ゲームオーバー状態ならキーイベント(リスタート用)のsubscriptionを定義

update

    case model.gameState of
        GameOver ->
            case msg of
                KeyDown keyType ->
                    case keyType of
                        Space ->
                            initialModel ()

                        Other ->
                            ( model
                            , Cmd.none
                            )

                Blinking _ ->
                    ( model
                    , Cmd.none
                    )

        Playing ->

GameStateのパターンマッチを追加
GameOver状態でSpaceを押すと初期状態に戻りリスタート出来るようにした

パターンマッチがネストするのちょっとどうにか出来ないかなーとここで思った

view

view : Model -> Html Msg
view model =
    let
        rowBaseSplitedBox =
            splitBox 7 model.boxList
    in
    div []
        [ div [] (List.map showBoxRow rowBaseSplitedBox)
        , div [] [ text <| showGameMessage model.gameState ]
        ]


showGameMessage : GameState -> String
showGameMessage gameState =
    case gameState of
        Playing ->
            "PRESS 'SPACE' TO STOP BOX"

        GameOver ->
            "GAME OVER! PRESS 'SPACE' TO TRY AGAIN"

ゲーム進行中はスペースで止められること
ゲームオーバー時はSPACEでリスタートできる
をメッセージとして出すようにした
f:id:nekorollykk:20200927004245p:plain

まとめ

  • 状態をもたせてパターンマッチして簡単に処理できた
  • パターンマッチネストするケースもう少しきれいに書きたい
  • 調べたけど速度の変更がイマイチうまく行かなかった
    そもそも速度はsubscriptionでいいのか?
    Process.sleepを組み合わせてTaskでどうにかするのか?
    そもそもラグが生まれるのはupdateの問題だからではないのか?
    BlinkingkeyDownのタイミングの問題
    あたりをぐるぐる悩んでた

次回はこのあたりをなんとかクリアしたい