Compare commits
3 Commits
781c06a987
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| c14bcecccc | |||
| 572726df7c | |||
| 7342f6dbc8 |
@@ -4,7 +4,8 @@
|
||||
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
|
||||
:url "https://www.eclipse.org/legal/epl-2.0/"}
|
||||
:dependencies [[org.clojure/clojure "1.11.1"]
|
||||
[seesaw "1.5.0"]]
|
||||
[seesaw "1.5.0"]
|
||||
[com.formdev/flatlaf "3.2.5"]]
|
||||
:main ^:skip-aot age-of-sail.core
|
||||
:target-path "target/%s"
|
||||
:profiles {:uberjar {:aot :all
|
||||
|
||||
@@ -8,12 +8,10 @@
|
||||
|
||||
(def revenge (ref {:position [0. 0.]
|
||||
:name "Revenge"
|
||||
:heading (normalize [1.0 1.0])
|
||||
:heading (normalize [0. 1.0])
|
||||
:slots [{:type :downwind-sail :length 2 :furl 1.0}]
|
||||
:velocity [1. 0.]}))
|
||||
|
||||
(reset! ships [virginia-woolfe revenge])
|
||||
|
||||
(show-ui)
|
||||
|
||||
@tracked-ship
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
(ns age-of-sail.core
|
||||
(:require [age-of-sail.vec2 :refer :all]
|
||||
(:require [age-of-sail.simulation :refer :all]
|
||||
[age-of-sail.vec2 :refer :all]
|
||||
[seesaw.core :refer :all]
|
||||
[seesaw.bind :as b]
|
||||
[clojure.math :as math]))
|
||||
|
||||
(def tickrate 100)
|
||||
(def hardcoded-wind [0.1 3.0]) ;; A strong easternly wind!
|
||||
[seesaw.bind :as b])
|
||||
(:import com.formdev.flatlaf.FlatDarculaLaf))
|
||||
;; Wind
|
||||
(def hardcoded-winds '({:strength [0.1 3.0] :position [0. 0.]})) ;; A strong easternly wind!
|
||||
(defn sum-wind
|
||||
"Wind at x is the sum of each wind force divided by its distance away"
|
||||
[x winds]
|
||||
(reduce (fn [sum wind]
|
||||
(let [dist (len (sub x (:position wind)))]
|
||||
(if (> dist 1.0)
|
||||
(add sum (scale (:strength wind) (/ (len (sub x (:position wind))))))
|
||||
(add sum (:strength wind)))))
|
||||
(zero)
|
||||
winds))
|
||||
;; Ships
|
||||
(defonce ships (atom []))
|
||||
|
||||
(defn ship-names
|
||||
@@ -35,67 +46,53 @@
|
||||
force)))
|
||||
|
||||
(defn physics-step
|
||||
[ship wind]
|
||||
[ship winds]
|
||||
(dosync
|
||||
;; Update position
|
||||
(alter ship update :position add (scale (:velocity @ship) (/ tickrate)))
|
||||
;; linear dampening
|
||||
(alter ship update :velocity scale (- 1.0 (/ 0.5 tickrate)))
|
||||
;; wind force
|
||||
(alter ship update :velocity add (scale (downwind-sails-force @ship wind) (/ tickrate)))))
|
||||
(let [wind (sum-wind (:position @ship) winds)]
|
||||
;; Update position
|
||||
(alter ship update :position add (scale (:velocity @ship) (/ tickrate)))
|
||||
;; linear dampening
|
||||
(alter ship update :velocity scale (- 1.0 (/ 0.5 tickrate)))
|
||||
;; wind force
|
||||
(alter ship update :velocity add (scale (downwind-sails-force @ship wind) (/ tickrate))))))
|
||||
|
||||
(defn tick
|
||||
[ships]
|
||||
(doseq [ship ships]
|
||||
(-> ship (physics-step hardcoded-wind))))
|
||||
(-> ship (physics-step hardcoded-winds))))
|
||||
|
||||
;; Simulation controls
|
||||
(defonce program (atom :stopped))
|
||||
(defn game-loop
|
||||
[]
|
||||
(while (#{:running :paused} @program)
|
||||
(when (= :running @program)
|
||||
(tick @ships))
|
||||
(Thread/sleep (quot 1000 tickrate)))
|
||||
(when-not (compare-and-set! program :killed :stopped)
|
||||
(throw "Error: tried to stop a program that wasn't killed!")))
|
||||
|
||||
(defn pause-program
|
||||
[]
|
||||
(compare-and-set! program :running :paused))
|
||||
|
||||
(defn kill-program
|
||||
[]
|
||||
(compare-and-set! program :running :paused)
|
||||
(compare-and-set! program :paused :killed))
|
||||
|
||||
(defn start-program
|
||||
[]
|
||||
(when (= (first (reset-vals! program :running)) :stopped)
|
||||
(.start (Thread. game-loop))))
|
||||
(subscribe! #(tick @ships))
|
||||
|
||||
;; UI
|
||||
(FlatDarculaLaf/setup)
|
||||
(defn ignore-args
|
||||
[f]
|
||||
(fn [& _] (f)))
|
||||
|
||||
(def tracked-ship (atom nil))
|
||||
(def ship-pos (b/notify-later))
|
||||
(defn update-pos
|
||||
[]
|
||||
(let [ship @tracked-ship]
|
||||
(when ship
|
||||
(b/notify ship-pos (:position @ship)))))
|
||||
(subscribe! update-pos)
|
||||
|
||||
(defn simulation-controls
|
||||
[]
|
||||
(let [start-button (button :text "Start" :listen [:mouse-clicked (ignore-args start-program)])
|
||||
pause-button (button :text "Pause" :listen [:mouse-clicked (ignore-args pause-program)])
|
||||
kill-button (button :text "Kill" :listen [:mouse-clicked (ignore-args kill-program)])]
|
||||
kill-button (button :text "Kill" :listen [:mouse-clicked (ignore-args kill-program)])]
|
||||
(letfn [(start-enabled? [state] (contains? #{:stopped :paused} state))
|
||||
(pause-enabled? [state] (= :running state))
|
||||
(kill-enabled? [state] (contains? #{:running :paused} state))]
|
||||
(kill-enabled? [state] (contains? #{:running :paused} state))]
|
||||
(b/bind program (b/tee
|
||||
(b/bind (b/transform start-enabled?) (b/property start-button :enabled?))
|
||||
(b/bind (b/transform pause-enabled?) (b/property pause-button :enabled?))
|
||||
(b/bind (b/transform kill-enabled?) (b/property kill-button :enabled?))))
|
||||
(b/bind (b/transform kill-enabled?) (b/property kill-button :enabled?))))
|
||||
(config! start-button :enabled? (start-enabled? @program))
|
||||
(config! pause-button :enabled? (pause-enabled? @program))
|
||||
(config! kill-button :enabled? (kill-enabled? @program))
|
||||
(config! kill-button :enabled? (kill-enabled? @program))
|
||||
(horizontal-panel :items [start-button pause-button kill-button]))))
|
||||
|
||||
(defn ship-chooser
|
||||
@@ -106,16 +103,14 @@
|
||||
(flow-panel :items ["Ship Name" name])))
|
||||
|
||||
(defn format-position
|
||||
[ship]
|
||||
(apply format "X: %.2f Y: %.2f" (:position @ship)))
|
||||
[[x y]]
|
||||
(format "X: %.2f Y: %.2f" x y))
|
||||
|
||||
(defn ship-info
|
||||
[]
|
||||
(let [panel (vertical-panel :visible? false)
|
||||
position (label)]
|
||||
(b/bind tracked-ship (b/some identity)
|
||||
(b/tee
|
||||
(b/bind (b/transform format-position) (b/value position))))
|
||||
(b/bind ship-pos (b/transform format-position) (b/property position :text))
|
||||
(add! panel (flow-panel :items ["Position" position]))
|
||||
panel))
|
||||
|
||||
@@ -134,6 +129,7 @@
|
||||
show!)))
|
||||
|
||||
(defn -main
|
||||
[args]
|
||||
[]
|
||||
(start-program)
|
||||
(show-ui))
|
||||
|
||||
|
||||
34
src/age_of_sail/simulation.clj
Normal file
34
src/age_of_sail/simulation.clj
Normal file
@@ -0,0 +1,34 @@
|
||||
(ns age-of-sail.simulation)
|
||||
|
||||
(def tickrate 100)
|
||||
(def hooks (atom '()))
|
||||
(defn subscribe!
|
||||
"Adds a hook to the hooks"
|
||||
[f]
|
||||
(swap! hooks conj f))
|
||||
|
||||
(defonce program (atom :stopped))
|
||||
|
||||
(defn game-loop
|
||||
[]
|
||||
(while (#{:running :paused} @program)
|
||||
(when (= :running @program)
|
||||
(doseq [hook @hooks]
|
||||
(hook)))
|
||||
(Thread/sleep (quot 1000 tickrate)))
|
||||
(when-not (compare-and-set! program :killed :stopped)
|
||||
(throw "Error: tried to stop a program that wasn't killed!")))
|
||||
|
||||
(defn pause-program
|
||||
[]
|
||||
(compare-and-set! program :running :paused))
|
||||
|
||||
(defn kill-program
|
||||
[]
|
||||
(compare-and-set! program :running :paused)
|
||||
(compare-and-set! program :paused :killed))
|
||||
|
||||
(defn start-program
|
||||
[]
|
||||
(when (= (first (reset-vals! program :running)) :stopped)
|
||||
(.start (Thread. game-loop))))
|
||||
@@ -9,6 +9,10 @@
|
||||
[v1 v2]
|
||||
(mapv + v1 v2))
|
||||
|
||||
(defn sub
|
||||
[v1 v2]
|
||||
(mapv - v1 v2))
|
||||
|
||||
(defn scale
|
||||
[v & scalars]
|
||||
(mapv #(* (apply * scalars) %) v))
|
||||
|
||||
Reference in New Issue
Block a user