(ns frpong.core (:require [frpong.helpers :as h :refer (mult tap)] [cljs.core.async :refer [! chan put! close! sliding-buffer]] [domina :as dom :refer [log]]) (:require-macros [cljs.core.async.macros :as m :refer [go]] [frpong.core :refer (go-loop)])) ;; ;; Signal Diagram ;; ;; +---------------------+ ;; | +-------------+ | ;; | | | | ;; v v | | ;; +----------+ vel-chan | | ;; +-->|c-detector+----------+ | ;; | +----------+ | | ;; | +-----------------+ | ;; | | +-----------------+ ;; | | | | ;; | v v | ;; +---------+ frame-chan +------+ tick-chan | +----------+ pos-chan | ;; |frame-gen+------------>|ticker+-----------+-->|positioner+--------------+ ;; +---------+ +------+ +----------+ | ;; +---------------------+ ;; | ;; v ;; +----------+ ;; | renderer | ;; +----------+ (defn abs [x] (.abs js/Math x)) (defn tick-chan [frames] (let [c (chan)] (go (loop [prev (! c t)) (recur t)) (close! c)))) c)) (defn next-pos [[x y] [vel-x vel-y] tick] [(+ x (* vel-x tick)) (+ y (* vel-y tick))]) (defn ^:export frpong [] (let [width 800 height 400 padding 5 paddle-size 100 paddle-width 10 ball-radius 5 init-pos [50 100] init-vel [0.2 0.23] paddle-step 20 max-paddle-y (- height paddle-size) ef-paddle-width (+ paddle-width padding) [frames stop-frames] (h/frame-chan)] (defn ticker [frames game-state ticks] (let [ticks-in (tick-chan (h/diff-chan frames))] (go (loop [] (let [gs (! ticks (! pos-out pos-next)))) (defn paddle-positioner [keycodes pos-in pos-out] (let [keys (h/key-chan keycodes)] (go-loop (let [pos (! pos-out (condp = ( y (+ paddle-y padding)) (< y (- (+ paddle-y paddle-size) padding)))) (defn detect-x-collision [x y lpaddle-y rpaddle-y] (cond (< x ef-paddle-width) (if (in-y-range? y lpaddle-y) :collision-left :gameover) (> x (- width ef-paddle-width)) (if (in-y-range? y rpaddle-y) :collision-right :gameover) :else :moving)) (defn detect-y-collision [y] (cond (< y padding) :collision-left (> y (- height padding)) :collision-right :else :moving)) (defn collision? [x-state y-state] (or (= x-state :collision-left) (= x-state :collision-right) (= y-state :collision-left) (= y-state :collision-right))) (defn adjust-vel [state v] (condp = state :collision-left (abs v) :collision-right (- (abs v)) :moving v :gameover 0)) (go-loop (let [tick (! vel-out [vel-xn vel-yn]) (>! game-state (cond (= x-state :gameover) :gameover (collision? x-state y-state) :collision :else :moving))))) (defn renderer [ticks game-state pos pl-pos pr-pos] (let [ball-el (dom/by-id "ball") state-el (dom/by-id "state") lpaddle-el (dom/by-id "lpaddle") rpaddle-el (dom/by-id "rpaddle") fps-el (dom/by-id "fps")] (go-loop (let [fps (/ 1000 (