From 87ddaae0b12249681e344d41a157a3122c0104c5 Mon Sep 17 00:00:00 2001 From: rawley Date: Fri, 3 Jul 2026 14:41:22 -0600 Subject: [PATCH] MOVEMENT --- resources/public/sprite/projectile.png | Bin 0 -> 302 bytes src/frontend/il/app.cljs | 118 +++++++++++++++++++++---- src/frontend/il/entity.cljs | 20 +++++ src/il/constant.clj | 10 +-- src/il/core.clj | 13 +-- src/il/world.clj | 73 --------------- 6 files changed, 122 insertions(+), 112 deletions(-) create mode 100644 resources/public/sprite/projectile.png create mode 100644 src/frontend/il/entity.cljs delete mode 100644 src/il/world.clj diff --git a/resources/public/sprite/projectile.png b/resources/public/sprite/projectile.png new file mode 100644 index 0000000000000000000000000000000000000000..990ec49c09b0b65c434065cdd2017a505d7cd773 GIT binary patch literal 302 zcmeAS@N?(olHy`uVBq!ia0vp^{6Ngk!3HF+w~DI+DaPU;cPEB*=VV?2IV|apzK#qG z8~eHcB(eheoCO|{#S9F5he4R}c>anMpx|Lo7sn8d;M((sT!#!q*dCO9VY_ru+QWVQ z1C0tM_k$-79^8BV#ghjQ-rYItt3EMLlPO`;jAh5(ZoSmMEA$%m@;?cR8wWcGHs^hfUup zpKn{S%0XhYI0HB5^t+y3N>27adM3K)%?RQ?$QspNuPQFYkapG1Ui`_%>mKY*qRJub wIZJHYPt{1Ck9&VE@wELX7V%rj(#I>A`47I7J83)39_SqgPgg&ebxsLQ0K;Q*%m4rY literal 0 HcmV?d00001 diff --git a/src/frontend/il/app.cljs b/src/frontend/il/app.cljs index 0dbb784..188eac1 100644 --- a/src/frontend/il/app.cljs +++ b/src/frontend/il/app.cljs @@ -1,31 +1,111 @@ (ns frontend.il.app - (:require ["pixi.js" :as pixi] - [goog.dom :as dom])) + (:require + [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)) (def websocket (doto (js/WebSocket. (str "ws://" js/location.host "/ws")) (.addEventListener "open" #(println "Websocket opened.")) - (.addEventListener "message" #(println "Received message:" (.-data %))) + (.addEventListener "message" #(println "Received message​ " (.-data %))) (.addEventListener "close" #(println "Websocket closed.")))) -(defn ticker [game ticker-object] - (let [bunny (game :bunny)] - (set! (.-rotation bunny) (+ (.-rotation bunny) 0.01)))) +(def base-player {:meta {:type "player" + :name "Player" + :race "Human" + :class "Priest"} + :sprite "https://pixijs.com/assets/bunny.png" + :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] + (let [id (swap! entity-id inc)] + (swap! state update-in [:entities] conj {(-> id str keyword) entity}))) + +(defn- velocity-for-key-down [key] + (case key + "w" -1 + "s" 1 + "a" -1 + "d" 1 + {})) + +(defn handle-key [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))))) + +(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 ticker [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)))))) (defn init [] - (let [app (pixi/Application.) - width (.-width js/window.screen) + (let [width (.-width js/window.screen) height (.-height js/window.screen)] (.. app - (init #js {:width width :height height :backgroundColor 0x1099bb}) + (init #js {:width width :height height :backgroundColor 0x222}) (then (fn [_] (dom/appendChild (dom/getElement "app") (.-canvas app)) - (let [texture (.. pixi -Assets (load "https://www.sprite-ai.art/sprite-img/99fa1574-fc52-41a6-ab76-1bd75b5dc4b3/16x16-pixel-art-sprite-cookie-shaped-like-a-32x32-pixel-art-90b4.png"))] - (.. texture - (then (fn [loaded-texture] - (let [bunny (pixi/Sprite. loaded-texture)] - (set! (.. bunny -anchor -x) 0.5) - (set! (.. bunny -anchor -y) 0.5) - (set! (.. bunny -x) (/ width 2)) - (set! (.. bunny -y) (/ height 2)) - (.. app -stage (addChild bunny)) - (.. app -ticker (add (partial ticker {:bunny bunny}))))))))))))) + (init-input-handlers) + (add-entity {:x 500 :y 500 :sprite "https://pixijs.com/assets/bunny.png"}) + (-> (.-stage app) + (.addChild world-layer) + (.addChild entity-layer)) + (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)] + (swap! state update-in [:player] merge player) + (.. app -ticker (add ticker)))))))) diff --git a/src/frontend/il/entity.cljs b/src/frontend/il/entity.cljs new file mode 100644 index 0000000..3d169b6 --- /dev/null +++ b/src/frontend/il/entity.cljs @@ -0,0 +1,20 @@ +(ns frontend.il.entity + (:require ["pixi.js" :as pixi])) + +(defn render [render-info 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 + (load sprite) + (then (fn [loaded-texture] + (set! (.. entity-sprite -texture) loaded-texture) + (set! (.. entity-sprite -anchor -x) 0.5) + (set! (.. entity-sprite -anchor -y) 0.5) + (set! (.. entity-sprite -x) x) + (set! (.. entity-sprite -y) y) + (.. layer (addChild entity-sprite))))) + entity-sprite)) diff --git a/src/il/constant.clj b/src/il/constant.clj index 5940845..9700e0b 100644 --- a/src/il/constant.clj +++ b/src/il/constant.clj @@ -1,10 +1,4 @@ (ns il.constant) -(def world-width 4096) -(def world-height 4096) - -(def resources - ["zinc" "iron" "titanium" "wood" "sulfur"]) - -(def min-resources 10) -(def max-resources 28) +(def screen-width 800) +(def screen-height 600) diff --git a/src/il/core.clj b/src/il/core.clj index b0fb08f..4709ddf 100644 --- a/src/il/core.clj +++ b/src/il/core.clj @@ -5,19 +5,8 @@ [compojure.route :as route] [clojure.core.async :refer [! go-loop]] [clojure.data.json :as json] - [il.world :as w] - [il.constant :as consts] [hiccup.core :as h])) -(def world (-> {} - (w/generate-water consts/world-width consts/world-height) - (w/generate-resources - consts/resources - consts/min-resources - consts/max-resources - consts/world-width - consts/world-height))) - (defn ws-handler [_] (go-websocket [in out err] (go-loop [] @@ -28,7 +17,7 @@ (if-let [msg (! out (json/write-str world))) + (= msg "world") (>! out "Hello World!")) (println msg) (recur)) (println "Closed."))))) diff --git a/src/il/world.clj b/src/il/world.clj deleted file mode 100644 index 9414593..0000000 --- a/src/il/world.clj +++ /dev/null @@ -1,73 +0,0 @@ -(ns il.world - (:require - [il.util :refer [rand-int-range]] - [clojure.math :as math])) - -(defn- resource-size [amt] - (cond - (>= amt 4000) "huge" - (>= amt 2750) "large" - (>= amt 1500) "medium" - :else "small")) - -(defn- in-water [water point] - (let [[x y] point])) - -(defn- rand-int-range-seq [min max] - (repeatedly (partial rand-int-range min max))) - -(def region-division 4) - -;; I thought about using Perlin noise to generate the water, but I find it over kill. -;; Instead, we split the world width, world height by a "region size", and pick -;; a random coordinate in those to generate water. -(defn generate-water [world world-width world-height] - (let [region-width (/ world-width region-division) - region-height (/ world-height region-division) - water (reduce - (fn [water [xs ys]] - (let [rw (* region-width xs) - rh (* region-height ys)] - (if (< (rand) 0.25) ; Only generate water in 25% of regions - (let [[wx wy] [(rand-int-range (- rw region-width) rw) - (rand-int-range (- rh region-height) rh)] - spokes (rand-int-range 5 17) ; "spokes" are how many points around the initial water placement - points (reduce - (fn [points [angle radius]] - (let [sx (+ wx (* radius (math/cos angle))) - sy (+ wy (* radius (math/sin angle)))] - (conj points [(int sx) (int sy)]))) - [] - (mapv vector - (sort (take spokes (rand-int-range-seq 0 (* math/PI 2)))) - (take spokes (rand-int-range-seq 6 12))))] - (conj water (conj points (first points)))) ; Append first point to end of vector, to complete the polygon - water))) - [] - (for [xs (range 1 (inc region-division)) - ys (range 1 (inc region-division))] - [xs ys]))] - (assoc world :water water))) - - -(defn generate-resources [world resource-types min max world-width world-height] - (let [resources (reduce - (fn [resources [type amt]] - (reduce - (fn [resources _] - (let [x (rand-int-range 0 world-width) - y (rand-int-range 0 world-height) - amt (rand-int-range 1000 5000) - size (resource-size amt)] - (if (in-water (:water world) [x y]) - resources - (conj resources [x y type amt size])))) - resources - (range amt))) - [] - (for [rt resource-types - amt (take - (count resource-types) - (repeatedly (partial rand-int-range min max)))] - [rt amt]))] - (assoc world :resources resources)))