Create and provide a timeline of user input events (keyboard / mouse)
Make the container accessible from the game object after initialization (mostly for debugging) Implement some test code (moving a sprite around)
This commit is contained in:
parent
fcc86c782c
commit
dcc5d430e3
|
@ -8,10 +8,28 @@
|
|||
(slick/start-game "Test game"
|
||||
{:state :game
|
||||
:gs (gs/with-gs gs/gs-empty
|
||||
(gs/add-index :name)
|
||||
(gs/add-index :type)
|
||||
(gs/set-entity (gs/gen-id) {:type :sprite :x 50 :y 50 :image "res/man.png"}))}))
|
||||
(gs/set-entity (gs/gen-id) {:type :sprite :x 50 :y 50
|
||||
:image "res/man.png" :name :man}))}))
|
||||
|
||||
(defmethod slick/update-game :game [state input delta]
|
||||
(let [gsnew
|
||||
(gs/with-gs (:gs state)
|
||||
(let [idman (first (gs/q :name :man))
|
||||
man (gs/entity idman)]
|
||||
(loop [timeline (slick/input-timeline input)]
|
||||
(let [[ts events] (first timeline)
|
||||
timelinenext (next timeline)]
|
||||
(doseq [{:keys [type action x y]} events]
|
||||
(if (and (= type :mouse) (= action :move))
|
||||
(gs/set-entity idman (assoc man :x x :y y))))
|
||||
(if timelinenext
|
||||
(recur timelinenext)
|
||||
(slick/input-clear input ts)))))
|
||||
(gs/get-gs))]
|
||||
(assoc state :gs gsnew)))
|
||||
|
||||
(defmethod slick/update-game :game [state delta])
|
||||
(defmethod slick/render-game :game [state graphics]
|
||||
(gs/with-gs (:gs state)
|
||||
(doseq [sprite (for [spriteid (gs/q :type :sprite)] (gs/entity spriteid))]
|
||||
|
@ -20,9 +38,9 @@
|
|||
|
||||
(def ^:dynamic g nil)
|
||||
(def ^:dynamic s nil)
|
||||
(def ^:dynamic _container nil)
|
||||
(defmethod slick/eval-with-bindings :game [state game container f]
|
||||
(binding [g game s state _container container]
|
||||
|
||||
(defmethod slick/eval-with-bindings :default [state game f]
|
||||
(binding [g game s state]
|
||||
(let [gsnew (gs/with-gs (:gs state) (f) (gs/get-gs))]
|
||||
(slick/game-setstate game (assoc state :gs gsnew)))))
|
||||
|
||||
|
|
|
@ -1,12 +1,114 @@
|
|||
(ns hottub.slick
|
||||
(:import [org.newdawn.slick AppGameContainer])
|
||||
(:import [org.newdawn.slick AppGameContainer
|
||||
ControlledInputReciever
|
||||
KeyListener
|
||||
MouseListener
|
||||
Input])
|
||||
(:use ns-tracker.core
|
||||
[hottub.util :as u]
|
||||
[hottub.repl :as repl]
|
||||
[hottub.resource :as res]))
|
||||
[hottub.resource :as res]
|
||||
[hottub.timeline :as tln]))
|
||||
|
||||
(defmulti update-game (fn [state delta] (:state state)))
|
||||
(defmulti update-game (fn [state input delta] (:state state)))
|
||||
(defmulti render-game (fn [state graphics] (:state state)))
|
||||
(defmulti eval-with-bindings (fn [state game container f] (:state state)))
|
||||
(defmulti eval-with-bindings (fn [state game f] (:state state)))
|
||||
|
||||
(def slick-key-to-key
|
||||
{Input/KEY_0 :0
|
||||
Input/KEY_1 :1
|
||||
Input/KEY_2 :2
|
||||
Input/KEY_3 :3
|
||||
Input/KEY_4 :4
|
||||
Input/KEY_5 :5
|
||||
Input/KEY_6 :6
|
||||
Input/KEY_7 :7
|
||||
Input/KEY_8 :8
|
||||
Input/KEY_9 :9
|
||||
|
||||
Input/KEY_A :a
|
||||
Input/KEY_B :b
|
||||
Input/KEY_C :c
|
||||
Input/KEY_D :d
|
||||
Input/KEY_E :e
|
||||
Input/KEY_F :f
|
||||
Input/KEY_G :g
|
||||
Input/KEY_H :h
|
||||
Input/KEY_I :i
|
||||
Input/KEY_J :j
|
||||
Input/KEY_K :k
|
||||
Input/KEY_L :l
|
||||
Input/KEY_M :m
|
||||
Input/KEY_N :n
|
||||
Input/KEY_O :o
|
||||
Input/KEY_P :p
|
||||
Input/KEY_Q :q
|
||||
Input/KEY_R :r
|
||||
Input/KEY_S :s
|
||||
Input/KEY_T :t
|
||||
Input/KEY_U :u
|
||||
Input/KEY_V :v
|
||||
Input/KEY_W :w
|
||||
Input/KEY_X :x
|
||||
Input/KEY_Y :y
|
||||
Input/KEY_Z :z
|
||||
|
||||
Input/KEY_UP :up
|
||||
Input/KEY_DOWN :down
|
||||
Input/KEY_LEFT :left
|
||||
Input/KEY_RIGHT :right })
|
||||
|
||||
(def slick-mouse-to-mouse
|
||||
{Input/MOUSE_LEFT_BUTTON :left
|
||||
Input/MOUSE_MIDDLE_BUTTON :middle
|
||||
Input/MOUSE_RIGHT_BUTTON :right})
|
||||
|
||||
; input
|
||||
(defprotocol InputTimeline
|
||||
(input-timeline [this])
|
||||
(input-clear [this tsfirst]))
|
||||
|
||||
(defn- add-event [atln event]
|
||||
(swap! atln tln/timeline-insert (u/timestamp) event))
|
||||
|
||||
(defn- add-key-event [atln skey action]
|
||||
(if-let [key (get slick-key-to-key skey)]
|
||||
(add-event atln {:type :key :action action :key key})))
|
||||
|
||||
(defn- add-mousebutton-event [atln sbutton action x y]
|
||||
(if-let [button (get slick-mouse-to-mouse sbutton)]
|
||||
(add-event atln {:type :mouse :action action :button button :x x :y y})))
|
||||
|
||||
(defn input-create []
|
||||
(let [atln (atom (timeline-create))]
|
||||
(reify
|
||||
InputTimeline
|
||||
(input-timeline [this] @atln)
|
||||
(input-clear [this tsafter] (swap! atln timeline-after tsafter))
|
||||
|
||||
ControlledInputReciever
|
||||
(inputEnded [this])
|
||||
(inputStarted [this])
|
||||
(isAcceptingInput [this] true)
|
||||
(setInput [this input])
|
||||
|
||||
KeyListener
|
||||
(keyPressed [this skey c] (add-key-event atln skey :pressed))
|
||||
(keyReleased [this skey c] (add-key-event atln skey :released))
|
||||
|
||||
MouseListener
|
||||
(mouseClicked [this sbutton x y count]
|
||||
(add-mousebutton-event atln sbutton (if (= count 2) :double-click :click) x y))
|
||||
(mouseDragged [this oldx oldy newx newy])
|
||||
(mouseMoved [this oldx oldy newx newy] (add-event atln {:type :mouse
|
||||
:action :move
|
||||
:x newx
|
||||
:y newy
|
||||
:dx (- oldx newx)
|
||||
:dy (- oldy newy)}))
|
||||
(mousePressed [this sbutton x y] (add-mousebutton-event atln sbutton :down x y))
|
||||
(mouseReleased [this sbutton x y] (add-mousebutton-event atln sbutton :up x y))
|
||||
(mouseWheelMoved [this dy] (add-event atln {:type :mouse :action :wheel :distance dy})))))
|
||||
|
||||
(def modified-namespaces (ns-tracker ["src" "test"]))
|
||||
|
||||
|
@ -16,19 +118,38 @@
|
|||
:init create
|
||||
:constructors {[String clojure.lang.PersistentArrayMap] [String]}
|
||||
:state state
|
||||
:extends org.newdawn.slick.BasicGame)
|
||||
:extends org.newdawn.slick.BasicGame
|
||||
:implements [hottub.slick.InputTimeline])
|
||||
|
||||
(defn game-create [title state]
|
||||
[[title]
|
||||
{:state (ref state)
|
||||
:repl (repl/repl-create 9999)}])
|
||||
:repl (repl/repl-create 9999)
|
||||
:container (atom nil)
|
||||
:input (input-create)}])
|
||||
|
||||
(defn game-state-ref [this] (:state (.state this)))
|
||||
|
||||
(defn game-container [this] @(:container (.state this)))
|
||||
|
||||
(defn game-install-input [this]
|
||||
(let [input (:input (.state this))
|
||||
sinput (.getInput (game-container this))]
|
||||
(.addKeyListener sinput input)
|
||||
(.addMouseListener sinput input)
|
||||
input))
|
||||
|
||||
(defn game-uninstall-input [this]
|
||||
(let [input (:input (.state this))
|
||||
sinput (.getInput (game-container this))]
|
||||
(.removeKeyListener sinput input)
|
||||
(.removeMouseListener sinput input)))
|
||||
|
||||
(defn game-init [this container]
|
||||
(reset! (:container (.state this)) container)
|
||||
(game-install-input this)
|
||||
(repl/repl-start
|
||||
(:repl (.state this))
|
||||
(fn [f] (eval-with-bindings @(game-state-ref this) this container f))))
|
||||
(fn [f] (eval-with-bindings @(game-state-ref this) this f))))
|
||||
|
||||
(defn game-update [this container delta]
|
||||
(doseq [ns-sym (modified-namespaces)]
|
||||
|
@ -38,9 +159,9 @@
|
|||
(repl/repl-update (:repl (.state this)))
|
||||
(dosync
|
||||
(try
|
||||
(if-let [statenew (update-game @(game-state-ref this) delta)]
|
||||
(if-let [statenew (update-game @(game-state-ref this) (:input (.state this)) delta)]
|
||||
(ref-set (game-state-ref this) statenew))
|
||||
(catch Exception e (prn "Couldn't update" (:state @(game-state-ref this)) (.getMessage e))))))
|
||||
(catch Exception e (println "Couldn't update" (:state @(game-state-ref this)) (.getMessage e))))))
|
||||
|
||||
(defn game-render [this container graphics]
|
||||
(try
|
||||
|
@ -51,9 +172,15 @@
|
|||
(defn game-setstate [this statenew]
|
||||
(dosync (ref-set (game-state-ref this) statenew)))
|
||||
|
||||
(defmethod update-game :default [state delta])
|
||||
(defn game-input_timeline [this]
|
||||
(input-timeline (:input (.state this))))
|
||||
|
||||
(defn game-input_clear [this tsafter]
|
||||
(input-clear (:input (.state this)) tsafter))
|
||||
|
||||
(defmethod update-game :default [state input delta])
|
||||
(defmethod render-game :default [state graphics])
|
||||
(defmethod eval-with-bindings :default [state game container f] (f))
|
||||
(defmethod eval-with-bindings :default [state game f] (f))
|
||||
|
||||
(defn start-game [title startstate]
|
||||
(let [game (hottub.slick.Game. title startstate)
|
||||
|
|
|
@ -5,3 +5,6 @@
|
|||
(if (compare-and-set! a oldval newval)
|
||||
oldval
|
||||
(recur @a))))
|
||||
|
||||
(defn timestamp []
|
||||
(/ (System/nanoTime) 1000000))
|
||||
|
|
Loading…
Reference in a new issue