Screen stack support

- rename "game-state" to "screen" (like "title screen", "options screen", etc)
- rename :state to :id
- provide helpers to load a screen given a "spec", deserialize a screen back to a spec, manage a screen stack
- rework test repl helper to allow setting the screen directly, instead of tweaking game state
- have start-game take a spec by default
This commit is contained in:
Jeremy Penner 2013-03-18 23:31:10 -04:00
parent cc62dfb054
commit bf41a54aec
3 changed files with 78 additions and 31 deletions

View file

@ -2,17 +2,21 @@
(:require [hottub.slick :as slick]
[hottub.gs :as gs]
[hottub.resource :as res]
[hottub.timeline :as tln]))
[hottub.timeline :as tln]
[hottub.screen :as scr]))
(defn -main [& args]
(res/start-resource-expiry-thread)
(slick/start-game "Test game"
{:state :game
:gs (gs/update-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" :name :man}))}))
(slick/start-game "Test game" {:id :game}))
(defmethod scr/screen-from-spec :game [spec]
{:gs (gs/update-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" :name :man}))})
(defmethod scr/spec-from-screen :game [screen] {:id :game})
(defmethod slick/update-game :game [state inputtln delta]
(assoc state :gs (gs/update-gs (:gs state)
@ -26,11 +30,14 @@
(if-let [image (res/get-image (:image sprite))]
(.draw image (:x sprite) (:y sprite))))))
;;; repl helpers
(def ^:dynamic g nil)
(def ^:dynamic s nil)
(def ^:dynamic *newscreen* nil)
(defn reload [] (reset! *newscreen* (scr/reload-screen s)))
(defmethod slick/eval-with-bindings :default [state game f]
(binding [g game s state]
(binding [g game s state *newscreen* (atom nil)]
(let [gsnew (gs/update-gs (:gs state) (f))]
(slick/game-setstate! game (assoc state :gs gsnew)))))
(slick/game-setstate! game (or @*newscreen* (assoc state :gs gsnew))))))

39
src/hottub/screen.clj Normal file
View file

@ -0,0 +1,39 @@
(ns hottub.screen)
(defmulti screen-from-spec (fn [spec] (:id spec)))
(defmulti spec-from-screen (fn [screen] (:id screen)))
(defn- -screen-from-spec [spec] (assoc (screen-from-spec spec) :id (:id spec)))
(defn- -spec-from-screen [screen] (assoc (spec-from-screen screen) :id (:id screen)))
(defn push-screen [screenold spec]
(let [specold (-spec-from-screen screenold)
screen (-screen-from-spec spec)]
(update-in [::stack] conj specold)))
(defn pop-screen [screenold]
(let [stackold (::stack screenold)
specnew (peek stackold)
stacknew (pop stackold)
screen (-screen-from-spec specnew)]
(assoc screen ::stack stacknew)))
(defn push-modal-screen [screenold spec]
(let [screen (-screen-from-spec spec)]
(assoc screen ::modal screenold)))
(defn pop-modal-screen [screenold]
(::modal screenold))
(defn screen-behind-modal [screen]
(pop-modal-screen screen))
(defn update-screen [screenold screennew]
(assoc screennew ::stack (::stack screenold)))
(defn goto-screen [screenold spec]
(let [screen (-screen-from-spec spec)]
(assoc screen ::stack (::stack screenold))))
(defn reload-screen [screen]
(goto-screen screen (-spec-from-screen screen)))

View file

@ -8,11 +8,12 @@
[hottub.util :as u]
[hottub.repl :as repl]
[hottub.resource :as res]
[hottub.timeline :as tln]))
[hottub.timeline :as tln]
[hottub.screen :as scr]))
(defmulti update-game (fn [state input delta] (:state state)))
(defmulti render-game (fn [state graphics] (:state state)))
(defmulti eval-with-bindings (fn [state game f] (:state state)))
(defmulti update-game (fn [screen input delta] (:id screen)))
(defmulti render-game (fn [screen graphics] (:id screen)))
(defmulti eval-with-bindings (fn [screen game f] (:id screen)))
(def slick-key-to-key
{Input/KEY_0 :0
@ -119,14 +120,14 @@
:state state
:extends org.newdawn.slick.BasicGame)
(defn game-create [title state]
(defn game-create [title screen]
[[title]
{:state (atom state)
{:screen (atom screen)
:repl (repl/repl-create 9999)
:container (atom nil)
:input (input-create)}])
(defn game-state-atom [this] (:state (.state this)))
(defn game-screen-atom [this] (:screen (.state this)))
(defn game-container [this] @(:container (.state this)))
(defn game-install-input [this]
@ -147,7 +148,7 @@
(game-install-input this)
(repl/repl-start
(:repl (.state this))
(fn [f] (eval-with-bindings @(game-state-atom this) this f))))
(fn [f] (eval-with-bindings @(game-screen-atom this) this f))))
(defn game-update [this container delta]
(doseq [ns-sym (modified-namespaces)]
@ -158,29 +159,29 @@
(.printStackTrace e))))
(repl/repl-update (:repl (.state this)))
(try
(if-let [statenew (update-game @(game-state-atom this) (input-timeline-clear! (:input (.state this))) delta)]
(reset! (game-state-atom this) statenew))
(if-let [screennew (update-game @(game-screen-atom this) (input-timeline-clear! (:input (.state this))) delta)]
(reset! (game-screen-atom this) screennew))
(catch Exception e
(println "Couldn't update" (:state @(game-state-atom this)))
(println "Couldn't update" (:id @(game-screen-atom this)))
(.printStackTrace e))))
(defn game-render [this container graphics]
(try
(res/gc-expired-resources)
(render-game @(game-state-atom this) graphics)
(render-game @(game-screen-atom this) graphics)
(catch Exception e
(println "Couldn't render" (:state @(game-state-atom this)))
(println "Couldn't render" (:id @(game-screen-atom this)))
(.printStackTrace e))))
(defn game-setstate! [this statenew]
(reset! (game-state-atom this) statenew))
(defn game-setscreen! [this screennew]
(reset! (game-screen-atom this) screennew))
(defmethod update-game :default [state input delta])
(defmethod render-game :default [state graphics])
(defmethod eval-with-bindings :default [state game f] (f))
(defmethod update-game :default [screen input delta])
(defmethod render-game :default [screen graphics])
(defmethod eval-with-bindings :default [screen game f] (f))
(defn start-game [title startstate]
(let [game (hottub.slick.Game. title startstate)
(defn start-game [title spec]
(let [game (hottub.slick.Game. title (scr/goto-screen nil spec))
container (AppGameContainer. game 640 480 false)]
(.setAlwaysRender container true)
(.start container)