日曜プログラミング

休日趣味でやってるプログラミング関連記事をダラダラと

Screen の 切替

arcade-clj シリーズ。全体の目次はこちら

元記事での追加機能

  1. スペースが押されたら背景色を赤⇔緑で切替える

これを元記事では libGDX で言う Screen を 2 つ用意して切替えるようにしている。 機能だけで見れば別に Screen を 2 つ用意しなくても背景色を変更するだけでいいんだが、 せっかくなので play-clj でも Screen を二つ用意して切替えるようにする。

画面切替はどんなゲームでも発生するだろうしね。

作業

  1. 赤い画面と緑の画面二つ用意する
  2. スペースキー押されたら別の画面にする

赤い画面と緑の画面二つ用意する

これをやるのは defscreen を二つ準備し、:on-key-down イベントで スペースが押されたら Screen 切替処理を実施すれば良い。

Screen を二つ用意する為のもっと具体的な手順は play-clj のチュートリアル に詳しいのでコードが雰囲気では良くわからない場合はそちらを見て頂きたい。

(declare arcade-clj-game main-screen alt-screen)

(defscreen main-screen
  :on-show
  (fn [screen entities]
    (update! screen :renderer (stage))
    entities)

  :on-key-down
  (fn [screen entities]
    (condp = (:key screen)
      (key-code :space) (set-screen! arcade-clj-game alt-screen)   ;; (1)
      (key-code :escape) (app! :exit)
      entities))                                                   ;; (2)
  
  :on-render
  (fn [screen entities]
    (clear! 1 0 0 1)                                               ;; (3)
    (render! screen entities)))

(defscreen alt-screen
  :on-show
  (fn [screen entities]
    (update! screen :renderer (stage)))

  :on-key-down
  (fn [screen entities]
    (condp = (:key screen)
      (key-code :space) (set-screen! arcade-clj-game main-screen) ;; (1)
      (key-code :escape) (app! :exit)
      entities))                                                  ;; (2)

  :on-render
  (fn [screen entities]
    (clear! 0 1 0 1)                                              ;; (3)
    (render! screen entities)))

ポイントは 3 箇所。

  1. space が押されたら自分とは異なる Screen への切替処理をここに記述
  2. はすいません、今回とは直接関係ないけど condp のデフォルト節を入れておくのを忘れてたのでここで修正しました。 前回のスナップショットだとこれがなかったせいで ESC 以外を押しても異常終了の形でアプリが終わってました。
  3. は Screen の背景色指定。RGB 指定。

ところでイベントハンドラに渡してる関数って Clojure 的にはタダの関数だよね

何が言いたいかと言うとこの段階だと 1,3 以外って同じだよなあと思い、 イベントハンドラ関数を外に出したコードを書いてみたんだが、 最初の画面は出るもののスペース押すと以下の例外吐いて落ちた。

Exception in thread "LWJGL Application" com.badlogic.gdx.utils.GdxRuntimeException: java.lang.AssertionError: Assert failed: Attempted to set an invalid screen.

libGDX の AssertionError ですか。 まあ defscreen に渡す関数自体を使い回す事はほぼ無いだろうからここは深くは追わないようにしよう。 各スクリーンで処理がほぼ一緒に見えるのこういう段階だけだろうし。

defscreen もマクロでこれ評価すると実際は何か関数定義してるみたいだし。 コード追っかけてないが多分 reify なり proxy なりで Interface の実装クラス作ってるのは 何となく想像はつくけど。

github スナップショット

今回の記事終了時点のソースは以下から取得可能。

Release v0.5.0 · shinmuro/arcade-clj · GitHub

一見かなりバージョンアップした風だけど、元記事と番号を合わせる為だけのタグなので悪しからず。