May 24, 2015

Tetris in clojurescript and re-frame Part 6 - User Interaction

Event Handler

In the :initialise-db register handler we also define the following code:

(set! (.-onkeydown js/document) keydown)

This will call the keydown function whenever a key is pressed and the window has the focus. It looks like this:

(register-handler
  :keypressed
  (rf/after grid-changed-mw)
  (fn [app-state [_ e]]
    (condp = (.-keyCode e)
      37 (move-on-keypress app-state #(update-in (:cur-active app-state) [:x] - 1))
      38 (move-on-keypress app-state #(update-in (:cur-active app-state) [:o] (fn [old] (mod (+ 1 old) 4))))
      39 (move-on-keypress app-state #(update-in (:cur-active app-state) [:x] + 1))
      40 (move-on-keypress app-state #(update-in (:cur-active app-state) [:y] + 1))
      32 (do (dispatch [:move-one-down]) app-state)
      80 (do (if (:paused? app-state) (dispatch [:unpause-game]) (dispatch [:pause-game])) app-state)
      app-state)))

(defn keydown [e]
  (when (h/in? [32 37 38 39 40] (.-keyCode e)) (.preventDefault e))
  (dispatch [:keypressed e]))

We will only prevent the default action if the up, down, left, right, p or space key was pressed, then we call dispatch again and call the :keypressed handler.
This calls the move-on-keypress function which looks like this:

(defn move-on-keypress [app-state move-fn]
  (let [cur-active (:cur-active app-state)
        cur-grid (:grid-state app-state)
        remove-cur-grid (minios/draw-tet cur-active minios/tet-recipe 0 cur-grid)]
    (if (minios/is-move-allowed? (move-fn) cur-active cur-grid minios/tet-recipe)
      (assoc app-state :cur-active (move-fn) :grid-state (minios/draw-tet (move-fn) minios/tet-recipe 1 remove-cur-grid))
      app-state)))

It checks whether the move is allowed and if so, updates the app state:

  • :cur-active update the active tetriminio position
  • :grid-state the updated grid state with the moved active tetriminio

If the move is not allowed, it will return the unchanged app state, making sure we cannot move the tetrimino outside of the grid or over existing borders.

Space Key

If we press the space key we want to move the tetriminio down to the lowest allowed position. To do this we dispatch on the :move-one-down event with the regarding handler:

(register-handler
  :move-one-down
  (rf/after grid-changed-mw)
  (fn [app-state _]
    (when (minios/is-move-allowed? (update-in (:cur-active app-state) [:y] + 2) (:cur-active app-state)
                                   (:grid-state app-state) minios/tet-recipe)
      (dispatch [:move-one-down]))
    (move-on-keypress app-state #(update-in (:cur-active app-state) [:y] + 1))))

As long as the down move is allowed it will dispatch on itself again with the side effect of moving one position down.

P Key

The P key will pause or unpause the game, dispatching on :pause-game or :unpause-game respectively:

(register-handler
  :pause-game
  (fn [db _]
    (. (:timer db) (stop))
    (assoc db :paused? true)))

(register-handler
  :unpause-game
  (fn [db _]
    (. (:timer db) (start))
    (assoc db :paused? false)))

It just stops or starts the timer which controls the game speed and updates the app states :paused key.

Tags: tetris clojuresrcipt