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"
|
(slick/start-game "Test game"
|
||||||
{:state :game
|
{:state :game
|
||||||
:gs (gs/with-gs gs/gs-empty
|
:gs (gs/with-gs gs/gs-empty
|
||||||
|
(gs/add-index :name)
|
||||||
(gs/add-index :type)
|
(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]
|
(defmethod slick/render-game :game [state graphics]
|
||||||
(gs/with-gs (:gs state)
|
(gs/with-gs (:gs state)
|
||||||
(doseq [sprite (for [spriteid (gs/q :type :sprite)] (gs/entity spriteid))]
|
(doseq [sprite (for [spriteid (gs/q :type :sprite)] (gs/entity spriteid))]
|
||||||
|
@ -20,9 +38,9 @@
|
||||||
|
|
||||||
(def ^:dynamic g nil)
|
(def ^:dynamic g nil)
|
||||||
(def ^:dynamic s nil)
|
(def ^:dynamic s nil)
|
||||||
(def ^:dynamic _container nil)
|
|
||||||
(defmethod slick/eval-with-bindings :game [state game container f]
|
(defmethod slick/eval-with-bindings :default [state game f]
|
||||||
(binding [g game s state _container container]
|
(binding [g game s state]
|
||||||
(let [gsnew (gs/with-gs (:gs state) (f) (gs/get-gs))]
|
(let [gsnew (gs/with-gs (:gs state) (f) (gs/get-gs))]
|
||||||
(slick/game-setstate game (assoc state :gs gsnew)))))
|
(slick/game-setstate game (assoc state :gs gsnew)))))
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,114 @@
|
||||||
(ns hottub.slick
|
(ns hottub.slick
|
||||||
(:import [org.newdawn.slick AppGameContainer])
|
(:import [org.newdawn.slick AppGameContainer
|
||||||
|
ControlledInputReciever
|
||||||
|
KeyListener
|
||||||
|
MouseListener
|
||||||
|
Input])
|
||||||
(:use ns-tracker.core
|
(:use ns-tracker.core
|
||||||
|
[hottub.util :as u]
|
||||||
[hottub.repl :as repl]
|
[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 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"]))
|
(def modified-namespaces (ns-tracker ["src" "test"]))
|
||||||
|
|
||||||
|
@ -16,19 +118,38 @@
|
||||||
:init create
|
:init create
|
||||||
:constructors {[String clojure.lang.PersistentArrayMap] [String]}
|
:constructors {[String clojure.lang.PersistentArrayMap] [String]}
|
||||||
:state state
|
:state state
|
||||||
:extends org.newdawn.slick.BasicGame)
|
:extends org.newdawn.slick.BasicGame
|
||||||
|
:implements [hottub.slick.InputTimeline])
|
||||||
|
|
||||||
(defn game-create [title state]
|
(defn game-create [title state]
|
||||||
[[title]
|
[[title]
|
||||||
{:state (ref state)
|
{: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-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]
|
(defn game-init [this container]
|
||||||
|
(reset! (:container (.state this)) container)
|
||||||
|
(game-install-input this)
|
||||||
(repl/repl-start
|
(repl/repl-start
|
||||||
(:repl (.state this))
|
(: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]
|
(defn game-update [this container delta]
|
||||||
(doseq [ns-sym (modified-namespaces)]
|
(doseq [ns-sym (modified-namespaces)]
|
||||||
|
@ -38,9 +159,9 @@
|
||||||
(repl/repl-update (:repl (.state this)))
|
(repl/repl-update (:repl (.state this)))
|
||||||
(dosync
|
(dosync
|
||||||
(try
|
(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))
|
(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]
|
(defn game-render [this container graphics]
|
||||||
(try
|
(try
|
||||||
|
@ -51,9 +172,15 @@
|
||||||
(defn game-setstate [this statenew]
|
(defn game-setstate [this statenew]
|
||||||
(dosync (ref-set (game-state-ref 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 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]
|
(defn start-game [title startstate]
|
||||||
(let [game (hottub.slick.Game. title startstate)
|
(let [game (hottub.slick.Game. title startstate)
|
||||||
|
|
|
@ -5,3 +5,6 @@
|
||||||
(if (compare-and-set! a oldval newval)
|
(if (compare-and-set! a oldval newval)
|
||||||
oldval
|
oldval
|
||||||
(recur @a))))
|
(recur @a))))
|
||||||
|
|
||||||
|
(defn timestamp []
|
||||||
|
(/ (System/nanoTime) 1000000))
|
||||||
|
|
Loading…
Reference in a new issue