diff --git a/src/frontend/il/app.cljs b/src/frontend/il/app.cljs index 188eac1..a9f4feb 100644 --- a/src/frontend/il/app.cljs +++ b/src/frontend/il/app.cljs @@ -3,13 +3,9 @@ [clojure.string :as str] ["pixi.js" :as pixi] [goog.dom :as dom] - [frontend.il.entity :as entity])) - -(def app - (pixi/Application.)) - -(def stage - (.-stage app)) + [cljs.math :as math] + [frontend.il.entity :as entity] + [frontend.il.update :as update])) (def websocket (doto (js/WebSocket. (str "ws://" js/location.host "/ws")) (.addEventListener "open" #(println "Websocket opened.")) @@ -24,22 +20,30 @@ :speed 10 :renderable nil}) -(def world-layer - (pixi/Container.)) - -(def entity-layer - (pixi/Container.)) - -(def entity-id (atom 1)) -(def state (atom {:player {:movement {:x 0 :y 0} :keymap {:w 0 :a 0 :s 0 :d 0}} - :entities {}})) - -(def lerp 0.1) - -(defn add-entity [entity] +(defn add-entity [state entity-id entity] (let [id (swap! entity-id inc)] (swap! state update-in [:entities] conj {(-> id str keyword) entity}))) +(defn on-shoot [state entity-id player-sprite e] + (.log js/console e) + (let [speed 15 + image "/sprite/projectile.png" + start-x (.-x player-sprite) + start-y (.-y player-sprite) + target-x (.. e -global -x) + target-y (.. e -global -y) + dx (- start-x target-x) + dy (- start-y target-y) + h (js/Math.sqrt (+ (* dx dx) (* dy dy))) + vx (* (/ dx h) speed) + vy (* (/ dy h) speed)] + (.log js/console "tx " target-x " ty " target-y " dx " dx " dy " dy " h " h) + (add-entity state entity-id {:sprite image + :hyp h + :x start-x + :y start-y + :velocity {:x vx :y vy}}))) + (defn- velocity-for-key-down [key] (case key "w" -1 @@ -48,64 +52,81 @@ "d" 1 {})) -(defn handle-key [func event] +(defn handle-key [state func event] (let [key (-> event .-key str/lower-case) vel (func key)] (swap! state update-in [:player :keymap] assoc (keyword key) vel) - (print @state) (let [{left :d right :a up :w - down :s} (get-in @state [:player :keymap])] - (swap! state update-in [:player :movement] assoc :x (+ left right) :y (+ up down))))) + down :s} (get-in @state [:player :keymap]) + speed (get-in @state [:player :speed] 5)] + (swap! state update-in [:player :velocity] assoc + :x (* speed (+ left right)) + :y (* speed (+ up down)))))) -(defn init-input-handlers [] - (js/window.addEventListener "keydown" (partial handle-key velocity-for-key-down)) - (js/window.addEventListener "keyup" (partial handle-key (fn [_] 0)))) +(defn init-input-handlers [state] + (js/window.addEventListener "keydown" (partial handle-key state velocity-for-key-down)) + (js/window.addEventListener "keyup" (partial handle-key state (fn [_] 0)))) -(defn ticker [t] +(defn ticker [app state layers t] (let [dt (.-deltaTime t) - {entities :entities - player :player} @state] - (doseq [k (keys entities)] - (let [{:keys [x y renderable] - :as e} (entities k)] - (if-not (nil? renderable) - (do - (set! (.. renderable -x) x) - (set! (.. renderable -y) y)) - (let [renderable (entity/render {:layer entity-layer} e)] - (swap! state update-in [:entities k] assoc :renderable renderable))))) - (when-not (nil? (:renderable player)) - (let [{dx :x dy :y} (:movement player) - {renderable :renderable - speed :speed} player - new-x (+ (* dt speed dx) (.-x renderable)) - new-y (+ (* dt speed dy) (.-y renderable))] - (set! (.. renderable -x) new-x) - (set! (.. renderable -y) new-y) - (swap! state update-in [:player] assoc :x new-x :y new-y) - (let [sx (- (/ (.. app -screen -width) 2) new-x) - sy (- (/ (.. app -screen -height) 2) new-y)] - (set! (.. world-layer -x) sx) - (set! (.. world-layer -y) sy)))))) + {world-layer :world-layer} layers + {:keys [player entities] + :as latest-state} (-> @state + (update/update-entities dt layers) + (update/update-player dt layers) + (->> (swap! state merge))) + sx (- (/ (.. app -screen -width) 2) (player :x)) + sy (- (/ (.. app -screen -height) 2) (player :y)) + entity-renderables (->> (vals entities) + (remove #(nil? (% :renderable))) + (vector)) + world-renderables (vector {:renderable world-layer :x sx :y sy}) + player-renderables (vector player + (assoc player :renderable (:shootbox player))) + things-to-update (conj player-renderable entity-renderables world-renderables)] + (println latest-state) + (println @state) + (println (count things-to-update)) + (doseq [thing things-to-update] + (let [{renderable :renderable + x :x + y :y} thing] + (println "Rendering thing " renderable x y) + (set! (.. renderable -x) x) + (set! (.. renderable -y) y))))) (defn init [] - (let [width (.-width js/window.screen) - height (.-height js/window.screen)] + (let [app (doto (pixi/Application.) + (.init {:resizeTo js/window :autoDensity true :resolution (.-devicePixelRatio js/window)})) + state (atom {:player {:velocity {:x 0 :y 0} :keymap {:w 0 :a 0 :s 0 :d 0}} + :entities {}}) + entity-id (atom 1) + width (.-width js/window.screen) + height (.-height js/window.screen) + stage (.-stage app) + world-layer (pixi/Container.) + entity-layer (pixi/Container.) + ui-layer (pixi/Container.) + layers {:entity-layer entity-layer + :world-layer world-layer + :ui-layer ui-layer}] (.. app (init #js {:width width :height height :backgroundColor 0x222}) (then (fn [_] (dom/appendChild (dom/getElement "app") (.-canvas app)) - (init-input-handlers) - (add-entity {:x 500 :y 500 :sprite "https://pixijs.com/assets/bunny.png"}) - (-> (.-stage app) - (.addChild world-layer) - (.addChild entity-layer)) + (init-input-handlers state) + (add-entity state entity-id {:x 500 :y 500 :sprite "https://pixijs.com/assets/bunny.png"}) + (-> stage + (.addChild world-layer) ; Bottom of the layer stack + (.addChild entity-layer) ; Middle of the layer stack + (.addChild ui-layer)) ; Top of the layer stack (let [e (merge {:x (-> width (/ 2) (int)) - :y (-> height (/ 2) (int))} - base-player) - p (entity/render {:layer entity-layer} e) - player (assoc e :renderable p)] + :y (-> height (/ 2) (int))} + base-player) + p (entity/render entity-layer e) + sb (entity/player-shoot-box entity-layer (partial on-shoot state entity-id p)) + player (assoc e :renderable p :shootbox sb)] (swap! state update-in [:player] merge player) - (.. app -ticker (add ticker)))))))) + (.. app -ticker (add (partial ticker app state layers))))))))) diff --git a/src/frontend/il/entity.cljs b/src/frontend/il/entity.cljs index 3d169b6..3e8139a 100644 --- a/src/frontend/il/entity.cljs +++ b/src/frontend/il/entity.cljs @@ -1,11 +1,10 @@ (ns frontend.il.entity (:require ["pixi.js" :as pixi])) -(defn render [render-info entity] +(defn render [layer entity] (let [{x :x y :y sprite :sprite} entity - {^js layer :layer} render-info entity-sprite (pixi/Sprite.)] (print "Creating sprite: " sprite " for entity " entity) (.. pixi -Assets @@ -16,5 +15,16 @@ (set! (.. entity-sprite -anchor -y) 0.5) (set! (.. entity-sprite -x) x) (set! (.. entity-sprite -y) y) - (.. layer (addChild entity-sprite))))) + (.addChild layer entity-sprite)))) entity-sprite)) + +(defn player-shoot-box [layer callback] + (let [g (pixi/Graphics.) + width (- (.-width js/window.screen) 80) + height (- (.-height js/window.screen) 80)] + (set! (.-eventMode g) "static") + (-> g + (.on "pointertap" callback) + (.rect (/ (- width) 2) (/ (- height) 2) width height) + (.fill #js {:color "red" :alpha 0.1}) + (->> (.addChild layer))))) diff --git a/src/frontend/il/update.cljs b/src/frontend/il/update.cljs new file mode 100644 index 0000000..f53f505 --- /dev/null +++ b/src/frontend/il/update.cljs @@ -0,0 +1,33 @@ +(ns frontend.il.update + (:require + [frontend.il.entity :as entity])) + +(defn apply-velocity [e dt] + (if (contains? e :velocity) + (let [{x :x y :y vel :velocity} e + {vx :x vy :y} vel + dtvx (* dt vx) + dtvy (* dt vy)] + (assoc e :x (+ x dtvx) :y (+ y dtvy))) + e)) + +(defn apply-renderable [e layers] + (if-not (contains? e :renderable) + (let [{entity-layer :entity-layer} layers + renderable (entity/render entity-layer e)] + (assoc e :renderable renderable)) + e)) + +(defn update-entities [state dt layers] + (let [{entities :entities} state + new-entities (update-vals entities #(-> % + (apply-renderable layers) + (apply-velocity dt)))] + (assoc state :entities new-entities))) + +(defn update-player [state dt layers] + (let [{player :player} state + updated-player (-> player + (apply-renderable layers) + (apply-velocity dt))] + (assoc state :player updated-player)))