Skip to content

Instantly share code, notes, and snippets.

@saikyun
Last active October 6, 2019 06:21
Show Gist options
  • Save saikyun/0be79a504149a6970c590dd49194105a to your computer and use it in GitHub Desktop.
Save saikyun/0be79a504149a6970c590dd49194105a to your computer and use it in GitHub Desktop.
hydrate/dehydrate arcadia
;; 1. Create an empty game object called "Beholder"
;; 2. Create a cube and add it as a child to the Beholder
;; 3. Run `(dehydrate-all!)`
;; 4. Move the created cube, then rotate it
;; 5. Run `(hydrate-all!)`
;; 6. Boom
(ns game.hydration
(:require [arcadia.core :refer :all]
[clojure.string :as str]
[arcadia.linear :as l]
[arcadia.introspection :as i])
(:import [UnityEngine Component Vector3 Rigidbody]))
(defonce dehydrated-data (atom {}))
(defn try-get-set-value
"Adds some needed error handling (ignoring) to `Get/SetInstanceFieldOrProperty`."
[obj prop]
(try
(let [v (clojure.lang.Reflector/GetInstanceFieldOrProperty obj prop)]
(clojure.lang.Reflector/SetInstanceFieldOrProperty obj prop v))
(catch System.NotSupportedException e
#_ (prn e))
;; For some reason `i/properties` find fields/properties that aren't invokeable
(catch System.ArgumentException e
#_ (prn e))
(catch System.Reflection.TargetInvocationException e
;; Lets ignore properties that has been deprecated
(when-not (some-> (.-InnerException e)
.-Message
(str/includes? "has been deprecated"))
(prn e)))))
(defn get-props
[obj]
(let [prop-names (map #(.-Name %) (i/properties (type obj)))]
{(type obj)
(into []
(filter
some?
(for [prop prop-names]
(when (try-get-set-value obj prop)
(symbol prop)))))}))
(defn get-props-from-type
"You can use this if you want to get a map with
a component type and all gettable properties
I'd recommend you remove the properties
you don't care about afterwards."
[component-type]
(let [go (GameObject.)
component (cmpt+ go component-type)
props (get-props component)]
(retire go)
props))
;; An example on how to automatically generate
;; properties from a component type
#_(def hydratable-components
"You can add any component here
You can also merge data from `get-props-from-type`"
(merge {UnityEngine.Transform ['localEulerAngles
'localPosition
'localRotation
'localScale]}
(get-props-from-type Rigidbody)))
(def hydratable-components
"You can add any component here
You can also merge data from `get-props-from-type`"
{UnityEngine.Transform ['localEulerAngles
'localPosition
'localRotation
'localScale]
Rigidbody ['angularDrag
'angularVelocity
'centerOfMass
'collisionDetectionMode
'constraints
'detectCollisions
'drag
'mass
'tag
'useGravity
'velocity]})
(defn gen-get-func [t attrs]
(let [component (gensym "component")]
`(fn [~(with-meta component {:tag (symbol (str t))})]
~(into {} (for [a attrs] `['~a (.. ~component ~a)])))))
(defn gen-set-func [t attrs]
(let [component (gensym "component")]
`(fn [~(with-meta component {:tag (symbol (str t))}) ~'values]
~@(for [a attrs]
`(set! (.. ~component ~a) (~'values '~a))))))
(def getters (->> hydratable-components
(map #(vector
(first %)
(eval (gen-get-func (first %) (second %)))))
(into {})))
(def setters (->> hydratable-components
(map #(vector
(first %)
(eval (gen-set-func (first %) (second %)))))
(into {})))
(defn dehydrate
"Takes a gameobject `go` and returns a vector of pairs,
containing the component types and their values."
[go]
(doall
(for [c (cmpts go Component)
:when (hydratable-components (type c))]
[c ((getters (type c)) c)])))
(defn hydrate!
[go cmpt-props]
(doseq [[c props] cmpt-props]
#_(doseq [[prop value] props]
(clojure.lang.Reflector/SetInstanceFieldOrProperty c (str prop) value))
((setters (type c)) c props)))
(defn dehydrate-all!
"Stores all component values of the childrens of `holder`."
([] (dehydrate-all! (object-named "Beholder")))
([holder]
(doseq [c (children holder)]
(let [hm (dehydrate c)]
(swap! dehydrated-data assoc c hm)))))
(defn hydrate-all!
"Goes through all children of the `holder`, and resets each child's component values."
([] (hydrate-all! (object-named "Beholder")))
([holder]
(doseq [c (children holder)]
(hydrate! c (get @dehydrated-data c)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment