From ab3ad38890b76f44dbde053cc761cb5c450eef84 Mon Sep 17 00:00:00 2001 From: oakes Date: Fri, 19 Sep 2014 12:24:49 -0400 Subject: [PATCH] Add time travel --- src/play_clj/core.clj | 45 ++++++++++++++++++++++++------------- src/play_clj/core_utils.clj | 18 +++++++++++++++ 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/src/play_clj/core.clj b/src/play_clj/core.clj index 73045b5..0601d09 100644 --- a/src/play_clj/core.clj +++ b/src/play_clj/core.clj @@ -43,12 +43,6 @@ (load "core_listeners") (load "core_utils") -(defn ^:private reset-changed! - [e-atom e-old e-new] - (when (and (not= e-old e-new) - (compare-and-set! e-atom e-old e-new)) - e-new)) - (defn ^:private normalize [entities] (some->> entities @@ -58,9 +52,26 @@ vec)) (defn ^:private wrapper - [screen screen-fn] + [screen-atom screen-fn] (screen-fn)) +(defn ^:private reset-changed! + [e-atom e-old e-new] + (when (and (not= e-old e-new) + (compare-and-set! e-atom e-old e-new)) + e-new)) + +(defn ^:private add-to-timeline! + [screen-atom entities] + (let [screen @screen-atom] + (when (:record? screen) + (swap! screen-atom + update-in + [:timeline] + #(conj (or %1 []) %2) + [(:total-time screen) entities]))) + entities) + (defn defscreen* [screen entities {:keys [on-show on-render on-hide on-pause on-resize on-resume on-timer] @@ -74,12 +85,12 @@ (meta func)) (wrapper screen) (reset-changed! entities old-entities) - (update-screen! @screen))))) + (update-screen! @screen))) + @entities)) execute-fn-on-gl! (fn [& args] (on-gl (apply execute-fn! args))) update-fn! (fn [func & args] - (doto (apply swap! screen func args) - update-screen!))] + (apply swap! screen func args))] {:screen screen :entities entities :execute-fn! execute-fn! @@ -92,7 +103,6 @@ (some-> @screen :world :object .dispose) ; set the initial values in the screen map (update-fn! assoc - :total-time 0 :execute-fn! execute-fn! :execute-fn-on-gl! execute-fn-on-gl! :update-fn! update-fn! @@ -105,10 +115,12 @@ (execute-fn! on-show) ; update the physics contact listener if a :world was created (some->> (contact-listener @screen options execute-fn!) - (update-fn! assoc :contact-listener))) + (update-fn! assoc :contact-listener) + update-screen!)) :render (fn [d] - (swap! screen #(assoc % :total-time (+ (:total-time %) d))) - (execute-fn! on-render :delta-time d)) + (swap! screen update-in [:total-time] #(+ (or %1 0) %2) d) + (->> (execute-fn! on-render :delta-time d) + (add-to-timeline! screen))) :hide #(execute-fn! on-hide) :pause #(execute-fn! on-pause) :resize (fn [w h] @@ -135,7 +147,7 @@ via the screen map. :on-render (fn [screen entities] (println (:delta-time screen)) ; time (ms) elapsed since last frame - (println (:total-time screen)) ; time (ms) elapsed since :on-show + (println (:total-time screen)) ; time (ms) elapsed since first :on-show entities) ; the screen was replaced :on-hide @@ -549,7 +561,8 @@ is the atom storing the screen map behind the scenes. Returns the updated (update! screen :renderer (stage))" [screen & args] - (apply (:update-fn! screen) assoc args)) + (doto (apply (:update-fn! screen) assoc args) + update-screen!)) (defn screen! "Runs a function defined in another screen. You may optionally pass a series diff --git a/src/play_clj/core_utils.clj b/src/play_clj/core_utils.clj index 3dea798..39248ed 100644 --- a/src/play_clj/core_utils.clj +++ b/src/play_clj/core_utils.clj @@ -78,6 +78,24 @@ specified path. ~name)] (u/call! p# ~k ~@options))) +(defn rewind! + "Returns the most recent entities vector saved in the timeline after removing +the last `steps` from it. The timeline is recorded by calling +`(update! screen :record? true)`. + +If there are 100 items saved in the timeline and `(rewind! screen 1)` is called, +it will remove the last one and return the entities vector from the 99th item. +If `steps` is invalid, nil is returned. + +If you want to do something more complex with the timeline than a simple rewind, +you can directly access it via `(:timeline screen)`. Each item inside it is a +vector containing a timestamp (seconds since the screen was first shown) and an +entities vector." + [{:keys [timeline update-fn!] :as screen} steps] + (when-let [[total-time entities] (get timeline (- (count timeline) steps 1))] + (update-fn! update-in [:timeline] subvec 0 (- (count timeline) steps 1)) + entities)) + ; static fields (defmacro scaling