Question

Let's say you're making a game. In the game you have some state about the world: score, number of players, enemies, etc. Let's say the game is sufficiently simple where it wouldn't be that bad to have globals in the file to update the world. Not optimal, but that's one way to do it. From an OO perspective you'd have a singleton object like a World object, with methods to query/update.

What would be best practice to detect key presses and pass the state of the presses into the engine? Would it be something like:

  (let [presses {}] 
     (.addEventListener "keypress" #(!set presses (into presses {%1 true})))
     (requestAnimationFrame js/window (fn [] (do-game presses))
Was it helpful?

Solution

Using core.async to capture and merge the events and translate them into game commands is a pretty cool way to go about this.

You can see an example in my snake game:
https://github.com/joakin/cnake/blob/master/src/cnake/ui.cljs#L140-L217

Once you have captured your input and transformed it into game events, you could pipe it to wherever you've got the game logic.

In my case, thats a go-loop that processes commands and contains the game state locally to that fn, and applies different functions to it and emits events for the UI to react to:
https://github.com/joakin/cnake/blob/master/src/cnake/ui.cljs#L140-L217

A more traditional approach would be to pass those commands to a function that would choose how to update an atom containing the game state.

There is a cool post about the core.async type of architecture on a game:
http://ragnard.github.io/2013/10/01/clojurecup-pong-async.html

OTHER TIPS

Yes that would work fine, Though there is no need for a singleton object for the global state, a simple atom will do fine. There are too many options for interactive Clojurescript programs such as this to recommend any particular one in this case.

There are so many ways to go about this, but I will show mine. I do this with baconjs and jquery wrappers for clojurescript.

(ns puzzle.input
  (:require [jayq.core :as j :refer [$]]
            [yolk.bacon :as b]))

(defn- read-key-input [e]
  (let [k (.-which e)]
    (condp = k
      38 :north
      40 :south
      37 :west
      39 :east
      :sit)))

(defn arrow-stream [$elem]
  (-> (.keydownE $elem)
      (b/filter (fn [e] (not= :sit (read-key-input e))))
      (b/do-action j/prevent)
      (b/map (fn [e] (read-key-input e)))))

I set up a stream of keyboard input on the body. I subscribe to that stream with something like this:

(-> (puzzle.input/arrow-stream (jayq.core/$ "body"))
    (yolk.bacon/on-value (fn [direction] (change-world direction))))

Now whenever the keypress is an arrow, the change-world function gets called with the direction.

https://github.com/Cicayda/yolk
https://github.com/Cicayda/yolk-jquery
https://github.com/ibdknox/jayq

You could try Om for this as it works by creating the application state in an atom which is rendered in RequestAnimationFrame

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top