ClojureのQuilでflappy bird。第3回

2023-04-20

「Nature of Code -Processingではじめる自然現象のシミュレーション」という本を入手して、1章完了まで進めたので、おさらいに、フラッピーバードを作成中。
Clojureを勉強中なので、こうした方がいいよとかあれば、ツッコミお願いします。

前回は、ベクトルの演算を作成して、自機が下に移動するようにしました。

今回は、落下するようにします。

移動にまつわる機能をまとめる

Nature of Code では、moverというクラスを作って、そこに位置、速度の値をもたせ、移動や表示に関するメソッドを生やしています。

それにならい、新たにmoverという名前空間を作って、そこに移動に関する関数をまとめます。データは :locatoin、:velocity というキーを持ったマップで扱うことにします。

src/flappy_bird/mover.clj、test/lappy_bird/mover_test.clj、を作成。mover_testに、コンストラクタ的な関数のテストを作ります。

(ns mover-test
  (:require [clojure.test :as t]
            [flappy-bird.mover :as mv]))

(t/deftest mover-test
  (t/testing "location と velocityをもつマップを返す"
    (t/is (= {:location [100 110]
              :velocity [0 0]}
             (mv/mover 100 110)))))
(ns flappy-bird.mover
  (:require [flappy-bird.qvector :as qv]
            [quil.middleware :as m]
            [quil.core :as q]
            [flappy-bird.mover :as mv]))

(defn mover [x y]
  {:location [x y]
   :velocity [0 0]})

それと、速度から位置を更新した新しい値を返すupdate関数、moverを描画するdisplay!関数も作ります。(「!」、Clojureの破壊的代入や副作用がある関数の名前にをつける慣習にならいました) display!関数はテストなし。

(t/deftest mover-test
  …略
  (t/testing "update"
    (t/testing "velocityをlocationに加算する"
      (t/is (= [-100 110] (-> (mv/mover -100 110)
                             mv/update
                             :location)))
      (t/is (= [-90 80] (-> (mv/mover -100 110)
                             (assoc :velocity [10 -30])
                             (mv/update)
                             :location))))))
(defn update [{:keys [location velocity] :as m}] 
  (assoc m :location (qv/add location velocity)))

(defn display [{[x y] :location}]
  (q/stroke 0)          ;;枠線は黒 
  (q/fill 170)          ;;中はグレー
  (q/rect x y 30 30))   ;;30 30 の四角を描画

それらを使い、setupで位置と速度を指定、update-stateではmover/updateを、draw-stateではmover/display!を呼ぶように変更。

(ns flappy-bird.core
  (:require [quil.core :as q]
            [quil.middleware :as m] 
            [flappy-bird.mover :as mv]))    ;;変更

(defn setup []
  (q/frame-rate 30)
  {:bird (-> (mv/mover 100 100)             ;;位置 100 100、速度 下向き1
             (assoc :velocity [0 1]))})

(defn update-state [{:keys [bird]}]
  {:bird (mv/update bird)})                  ;;速度もとに位置を更新

(defn draw-state [{:keys [bird]}]
  (q/background 240) 
  (mv/display! bird))                        ;;描画

…略

加速度を追加

ここまでは、移動に関する機能をまとめ直しただけで、画面の動きは変わらず等速運動です。

これを落下(等加速度運動)にするため、加速度もあつかえるようにマップにキー:accelerationを追加。

また、位置は速度によって、速度は加速度によって決まるので、update関数をそのように変更。

(t/testing "update"
  (t/testing "accelerationをvelocityに、velocityをlocationに加算する"
  …略
      (t/is (= {:location [-95 90]
                :velocity [5 -20]
                :acceleration [-5 10]}
               (-> (mv/mover -100 110)
                   (assoc :velocity [10 -30])
                   (assoc :acceleration [-5 10])
                   (mv/update)))))))
(defn mover [x y]
  {:location [x y]
   :velocity [0 0]
   :acceleration [0 0]})

(defn update [m] 
  (letfn [(ud [m to from]
           (clojure.core/update m to (partial qv/add (from m))))] 
    (-> m
        (ud :velocity :acceleration)
        (ud :location :velocity)))))

テストが成功したら、core.cljを変更。書き換えるのは、setupの1行だけ。

これで、update-stateが呼ばれるたびに、だんだん加速するようになります。

(defn setup []
  (q/frame-rate 30)
  {:bird (-> (mv/mover 100 100) 
             (assoc :acceleration [0 0.3M]))})      ;;加速度を下向き0.3にする

まとめ

今回は、落下の動きを作成しました。

次回は、キー操作で自機が浮上させます。