Open saikyun opened 6 years ago
I made a workaround to progress in the tutorial by creating an empty object and adding all GOs as children to it, and destroying all children on that GO when running setup again.
;; Game object to hold all generated objects
(def holder (object-named "Generated"))
(defn setup [parent]
(doseq [child (children parent)]
(destroy child))
;; Load the "fighter" prefab into the scene graph
(let [player (GameObject/Instantiate (Resources/Load "fighter"))]
(child+ holder player)
;; Set its name
(set! (.name player) "player")))
Here's a whole working example:
(ns fighter-tutorial.core
(:use arcadia.core arcadia.linear)
(:require [arcadia.sugar :as a]
[arcadia.scene :as scn])
(:import [UnityEngine Collider2D Physics
GameObject Input Rigidbody2D
Vector2 Mathf Resources Transform
Quaternion
Collision2D Physics2D]
ArcadiaState))
(defn bearing-vector [angle]
(let [angle (* Mathf/Deg2Rad angle)]
(v2 (Mathf/Cos angle) (Mathf/Sin angle))))
(defn abs-angle [v]
(* Mathf/Rad2Deg
(Mathf/Atan2 (.y v) (.x v))))
(defn controller-vector []
(v2 (Input/GetAxis "Horizontal")
(Input/GetAxis "Vertical")))
(defn wasd-key []
(or (Input/GetKey "w")
(Input/GetKey "a")
(Input/GetKey "s")
(Input/GetKey "d")))
(defn move-forward [^Rigidbody2D rb, distance]
(.MovePosition rb
(v2+ (.position rb)
(v2* (bearing-vector (.rotation rb))
distance))))
(def player-bullets-layer (UnityEngine.LayerMask/NameToLayer "player-bullets"))
(def enemy-bullets-layer (UnityEngine.LayerMask/NameToLayer "enemy-bullets"))
(defn player-movement-fixed-update [obj k]
(with-cmpt obj [rb Rigidbody2D]
(when (wasd-key)
(.MoveRotation rb (abs-angle (controller-vector)))
(set! (.angularVelocity rb) 0)
(.AddForce rb
(v2* (bearing-vector (.rotation rb))
3)))))
(def player-movement-role
{:fixed-update #'player-movement-fixed-update})
(defrole health-role
:state {:health 1}
(update [obj k]
(let [{:keys [health]} (state obj k)]
(when (<= health 0)
(destroy obj)))))
(defn damage [obj amt]
(update-state obj ::health update :health - amt))
(defrole bullet-collision
(on-trigger-enter2d [bullet, ^Collider2D collider, k]
(let [obj2 (.gameObject collider)]
(when (state obj2 ::health)
(damage obj2 1)
(destroy bullet)))))
(defrole lifespan-role
:state {:start System.DateTime/Now
:lifespan 0}
(update [obj k]
(let [{:keys [start lifespan]} (state obj k)]
(when (< lifespan (.TotalMilliseconds (.Subtract System.DateTime/Now start)))
(destroy obj)))))
(defrole bullet-movement-role
(fixed-update [bullet k]
(with-cmpt bullet [rb Rigidbody2D]
(move-forward rb 0.2))))
(def bullet-roles
{::movement bullet-movement-role
::lifespan lifespan-role
::collision bullet-collision})
(defn shoot-bullet [start bearing parent]
(let [bullet (GameObject/Instantiate
(Resources/Load "missile" GameObject))]
(with-cmpt bullet [rb Rigidbody2D,
tr Transform]
(child+ parent bullet)
(set! (.position tr) (v3 (.x start) (.y start) 1))
(.MoveRotation rb bearing)
(roles+ bullet
(-> bullet-roles
(assoc-in [::lifespan :state :start] System.DateTime/Now)
(assoc-in [::lifespan :state :lifespan] 2000)))
bullet)))
(defn shoot [obj layer parent]
(with-cmpt obj [rb Rigidbody2D]
(let [bullet (shoot-bullet (.position rb) (.rotation rb) parent)]
(set! (.layer bullet) layer)
bullet)))
(defrole player-shooting-role
(update [obj k]
(with-cmpt obj [rb Rigidbody2D]
(when (Input/GetKeyDown "space")
(shoot obj player-bullets-layer (parent obj))))))
(def player-roles
{::movement player-movement-role
::shooting player-shooting-role
::health (update health-role :state assoc :health 10)})
(defrole enemy-shooting-role
:state {:last-shot System.DateTime/Now}
(update [obj k]
(let [{:keys [target last-shot]} (state obj k)
now System.DateTime/Now]
(when (and (obj-nil target)
(< 1000 (.TotalMilliseconds (.Subtract now last-shot))))
(update-state obj k assoc :last-shot now)
(shoot obj enemy-bullets-layer (parent obj))))))
(defrole enemy-movement-role
:state {:target nil}
(fixed-update [obj k]
(when-let [target (obj-nil (:target (state obj k)))]
(a/let [(a/with-cmpt rb1 Rigidbody2D) obj
(a/o pos1 position, rot1 rotation) rb1
(a/with-cmpt (a/o pos2 position) Rigidbody2D) target
pos-diff (v2- pos2 pos1)
rot-diff (Vector2/SignedAngle
(bearing-vector rot1)
pos-diff)]
(.MoveRotation rb1
(+ rot1 (Mathf/Clamp -1 rot-diff 1)))))))
(def enemy-roles
{::shooting enemy-shooting-role
::movement enemy-movement-role
::health (update health-role :state assoc :health 10)})
(defn make-enemy [protagonist parent]
(let [enemy (GameObject/Instantiate (Resources/Load "villain" GameObject))]
(child+ parent enemy)
(roles+ enemy
(-> enemy-roles
(assoc-in [::movement :state :target] protagonist)
(assoc-in [::shooting :state :target] protagonist)))))
(defn setup []
;; Game object to hold all generated objects
(let [holder (object-named "Generated")]
(doseq [child (children holder)]
(destroy child))
;; Load the "fighter" prefab into the scene graph
(let [player (GameObject/Instantiate (Resources/Load "fighter"))]
(child+ holder player)
;; Set its name
(set! (.name player) "player")
(roles+ player player-roles)
(make-enemy player holder))))
@Saikyun Thanks for this, with your work-around and working example, was able to get through the tutorial with 2018.1.0f2.
@sogaiu Cool! I'm happy to hear that. :)
Commenting out:
(scn/register bullet ::bullet)
seems to be another work-around.
I think the current situation may have something to do with:
I got the impression that the label-registry gets out of sync with which bullet objects still exist -- arcadia.scene/register is used to track them, but arcadia.core/retire is used to directly get rid of them, so this leaves the label-registry referring to no longer valid bullet objects.
Or something like that :)
When trying to follow the tutorial (https://github.com/arcadia-unity/fighter-tutorial#building-the-players-avatar), I get the following error when running
(setup)
a second time:NullReferenceException Object reference not set to an instance of an object Arcadia.HookStateSystem.Lookup (/Users/jona/unity/arcadia-test-2/Assets/Arcadia/Helpers/HookStateSystem.cs:49)
This happens when
(scn/retire ::player)
is ran (I tested running setup once and then just running(scn/retire ::player)
in the repl).Haven't been able to figure out why.
I use macOS Sierra and Unity 2017.3.1f1. I've tried with arcadia that comes with this repo and with arcadia from the develop-branch.