Last active
October 6, 2019 06:21
-
-
Save saikyun/0be79a504149a6970c590dd49194105a to your computer and use it in GitHub Desktop.
hydrate/dehydrate arcadia
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; 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