-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Feature/wombat animation #361
base: develop
Are you sure you want to change the base?
Changes from 17 commits
72680f6
7e9a53b
d916d68
9267eab
2542bc0
e3968f6
a3a2ab2
7b93779
19b38f1
040ed74
0b4856a
761f9b8
5b5880c
9e0d221
f8801ae
c9b5065
13947f5
d37c819
e7ac7cb
d98d5dc
c3be54f
5605e94
c2d2621
c0811a8
e8cff8e
2bdfc70
3944c05
61f9c37
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
(ns wombats-web-client.components.arena | ||
(:require [re-frame.core :as re-frame] | ||
(:require [reagent.core :as reagent] | ||
[re-frame.core :as re-frame] | ||
[wombats-web-client.utils.canvas :as canvas])) | ||
|
||
(defonce spritesheet-png "/images/spritesheet.png") | ||
(defonce frame-time 20) | ||
|
||
(defn subscribe-to-spritesheet | ||
[img-name callback] | ||
|
@@ -300,25 +302,204 @@ | |
|
||
(js/console.log "Unhandled: " cell-type)))) | ||
|
||
(defn arena | ||
"Renders the arena on a canvas element, and subscribes to arena updates" | ||
[arena canvas-id] | ||
(let [canvas-element (.getElementById js/document canvas-id)] | ||
(when-not (nil? canvas-element) | ||
;; Calculate the width and height of each cell | ||
(def height (/ (canvas/height canvas-element) (count arena))) | ||
(def width (/ (canvas/width canvas-element) (count (get arena 0)))) | ||
|
||
;; Iterate through all of the arena rows | ||
(doseq [[y row] (map-indexed vector arena)] | ||
(doseq [[x cell] (map-indexed vector row)] | ||
|
||
(def x-coord (* x width)) | ||
(def y-coord (* y height)) | ||
|
||
(defn- draw-arena-canvas | ||
"Given a canvas element and the arena, draw the canvas" | ||
[{:keys [arena | ||
canvas-element]}] | ||
;; Calculate the width and height of each cell | ||
(let [height (/ (canvas/height canvas-element) (count arena)) | ||
width (/ (canvas/width canvas-element) (count (get arena 0)))] | ||
|
||
;; Iterate through all of the arena rows | ||
(doseq [[y row] (map-indexed vector arena)] | ||
(doseq [[x cell] (map-indexed vector row)] | ||
(let [x-coord (* x width) | ||
y-coord (* y height)] | ||
(draw-cell cell | ||
x-coord | ||
y-coord | ||
width | ||
height | ||
canvas-element)))))) | ||
|
||
(defn in? | ||
"Return true if coll contains elem" | ||
[elem coll] | ||
(some #(= elem %) coll)) | ||
|
||
(defn- get-step-size | ||
"Calculate the size of the step, as well as whether it is forward or back" | ||
[start end dimension-key] | ||
(let [start-pos (dimension-key start) | ||
end-pos (dimension-key end)] | ||
(- end-pos start-pos))) | ||
|
||
(defn- get-direction-key | ||
"Given the start and the end dimension object, calculate which direction | ||
has movement" | ||
[start end] | ||
(if (= (:x start) (:x end)) | ||
:y | ||
:x)) | ||
|
||
(defn get-animated-coord | ||
"Uses the progress of the animation to | ||
calculate where the wombats/zakano should be placed" | ||
[{:keys [x y width height direction-key animation-progress step-size]}] | ||
{:x (if (= direction-key :x) | ||
(* width (+ x (* (:progress animation-progress) step-size))) | ||
(* width x)) | ||
|
||
:y (if (= direction-key :y) | ||
(* height (+ y (* (:progress animation-progress) step-size))) | ||
(* height y))}) | ||
|
||
(defn- draw-arena-canvas-animated | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this method draws all cells (not just animated). If so, it would make sense to rename this method. |
||
"Given a canvas element and the arena - skip the animated items" | ||
[{:keys [arena | ||
canvas-element | ||
animated | ||
animation-progress]}] | ||
|
||
;; Calculate the width and height of each cell | ||
(let [height (/ (canvas/height canvas-element) (count arena)) | ||
width (/ (canvas/width canvas-element) (count (get arena 0))) | ||
animated-x (map #(get-in % [:end :x]) animated) | ||
animated-y (map #(get-in % [:end :y]) animated) | ||
animated-types (map #(get-in % [:cell :contents :type]) animated)] | ||
;; Iterate through all of the arena rows - draw non-animated items | ||
(doseq [[y row] (map-indexed vector arena)] | ||
(doseq [[x cell] (map-indexed vector row)] | ||
(let [x-coord (* x width) | ||
y-coord (* y height)] | ||
(when-not | ||
(and (in? x animated-x) | ||
(in? y animated-y) | ||
(in? (get-in cell [:contents :type]) animated-types)) | ||
(draw-cell cell | ||
x-coord | ||
y-coord | ||
width | ||
height | ||
canvas-element))))) | ||
|
||
;; run through animated items and apply the animation to them | ||
(doseq [item animated] | ||
(let [item-start (:start item) | ||
item-end (:end item) | ||
direction-key (get-direction-key item-start item-end) | ||
step-size (/ (get-step-size item-start | ||
item-end | ||
direction-key) frame-time) | ||
cell (:cell item) | ||
x (get-in item [:start :x]) | ||
y (get-in item [:start :y]) | ||
new-coords (get-animated-coord | ||
{:x x | ||
:y y | ||
:width width | ||
:height height | ||
:direction-key direction-key | ||
:animation-progress animation-progress | ||
:step-size step-size})] | ||
(when (<= (Math/abs (get-step-size item-start | ||
item-end | ||
direction-key)) 1) | ||
(draw-cell cell | ||
(:x new-coords) | ||
(:y new-coords) | ||
width | ||
height | ||
canvas-element))))) | ||
|
||
;; this when subtracts one from the end because the animation runs once | ||
;; before it gets a callback | ||
(when (<= (:progress animation-progress) (dec (:end animation-progress))) | ||
(.requestAnimationFrame js/window | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be cleaner to just do a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure how to do this - would you replace the .requestAnimationFrame ? and what would I call force-update on to make sure I could increment the animation progress? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What I'm thinking (might not actually work) is doing the following:
Then you're triggering a rerender every animation frame so you don't need to have any logic inside of the callback. Does that make sense? |
||
#(draw-arena-canvas-animated | ||
{:arena arena | ||
:canvas-element canvas-element | ||
:animated animated | ||
:animation-progress | ||
(update-in animation-progress | ||
[:progress] | ||
inc)})))) | ||
|
||
(defn arena | ||
"Renders the arena on a canvas element, and subscribes to arena updates" | ||
[arena canvas-id] | ||
(let [canvas-element (.getElementById js/document canvas-id)] | ||
(when-not (nil? canvas-element) | ||
|
||
(draw-arena-canvas {:arena arena | ||
:canvas-element canvas-element})))) | ||
|
||
(defn- add-locs | ||
"Add local :x and :y coordinates to arena matrix" | ||
[arena] | ||
(map-indexed | ||
(fn [y row] (map-indexed | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indentation is off in this function. |
||
(fn [x tile] (assoc tile :x x :y y)) | ||
row)) | ||
arena)) | ||
|
||
(defn- filter-arena | ||
"Filter the arena to return only nodes that contain one of the supplied types" | ||
([arena] (flatten arena)) | ||
([arena filters] | ||
(let [node-list (flatten arena)] | ||
(filter #(in? (get-in % [:contents :type]) filters) node-list)))) | ||
|
||
(defn- get-uuid | ||
[item] | ||
(get-in item [:contents :uuid])) | ||
|
||
(defn- dimensions | ||
[item] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function isn't needed since There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Answered down below |
||
{:x (:x item) | ||
:y (:y item)}) | ||
|
||
(defn- create-animations-vector | ||
"Input is two vectors of flatten-item responses, | ||
output is a vector of the animations" | ||
[prev-coords next-coords] | ||
(remove nil? | ||
(reduce (fn [out-vec prev] | ||
(conj out-vec | ||
(reduce | ||
(fn [obj next] ;; could use (first (filter (fn ...))) | ||
(if (and (= (get-uuid next) (get-uuid prev)) | ||
(not= (dimensions prev) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Down here, you could use another function you write that just compares the |
||
(dimensions next))) | ||
{:start (dimensions prev) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then down here, you'd pass
And you can completely get rid of cell. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh you answered my question up above - thanks 👍 |
||
:end (dimensions next) | ||
:progress (dimensions prev) | ||
:cell next} | ||
obj)) | ||
nil next-coords))) [] prev-coords))) | ||
|
||
|
||
(defn arena-history | ||
"Renders the arena on a canvas element, given the frames item and an index, | ||
allowing for animation between the frames" | ||
[{:keys [frames-vec frames-idx view-mode canvas-id]}] | ||
(let [prev-frame (get-in @frames-vec | ||
[(dec @frames-idx) | ||
:game/frame | ||
:frame/arena]) | ||
next-frame (get-in @frames-vec | ||
[@frames-idx | ||
:game/frame | ||
:frame/arena]) | ||
;; Get the coordinates of the animated items on the grid | ||
prev-coords (filter-arena (add-locs prev-frame) [:zakano :wombat]) | ||
next-coords (filter-arena (add-locs next-frame) [:zakano :wombat]) | ||
canvas-element (.getElementById js/document canvas-id) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be better to use |
||
animations (create-animations-vector prev-coords next-coords) | ||
animation-progress {:progress 0 | ||
:end frame-time}] | ||
(when-not (nil? canvas-element) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would just use |
||
(draw-arena-canvas-animated {:arena next-frame | ||
:canvas-element canvas-element | ||
:animated animations | ||
:animation-progress animation-progress})))) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,9 +9,11 @@ | |
;; Main Method | ||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
|
||
(defn render | ||
"Takes the simulator data object and the view mode key and renders the arena" | ||
[simulator-data simulator-view-mode] | ||
(defn render [simulator-frames simulator-index simulator-view-mode] | ||
|
||
(arena/arena (simulator-view-mode @simulator-data) canvas-id) | ||
(arena/arena-history | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be in |
||
{:frames-vec simulator-frames | ||
:frames-idx simulator-index | ||
:view-mode simulator-view-mode | ||
:canvas-id canvas-id}) | ||
[:canvas {:id canvas-id}]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I think this could still be done cleaner by combining
draw-arena-canvas
intodraw-arena-canvas-animated
.I think the best way would be to sort all of the elements by the order that they should be drawn (such that the animated elements are always drawn after the non-animated). You would need to merge in their x/y coordinates. Then you can simply draw the element at the correct location. Does that make sense?