Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Object reference not set to an instance of an object when running (scn/retire ::player) #10

Open
saikyun opened this issue May 14, 2018 · 5 comments

Comments

@saikyun
Copy link

saikyun commented May 14, 2018

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.

@saikyun
Copy link
Author

saikyun commented May 14, 2018

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")))

@saikyun
Copy link
Author

saikyun commented May 18, 2018

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))))

@sogaiu
Copy link

sogaiu commented Sep 7, 2018

@saikyun Thanks for this, with your work-around and working example, was able to get through the tutorial with 2018.1.0f2.

@saikyun
Copy link
Author

saikyun commented Sep 8, 2018

@sogaiu Cool! I'm happy to hear that. :)

@sogaiu
Copy link

sogaiu commented Sep 12, 2018

Commenting out:

(scn/register bullet ::bullet)

seems to be another work-around.

I think the current situation may have something to do with:

  • arcadia.scene maintains label-registry for lookup of objects to labels and labels to objects
  • label-registry is affected via arcadia.scene/{register,retire} which are both used in the tutorial
  • there are 3 calls to arcadia.core/retire (which is distinct from arcadia.scene/retire)
  • arcadia.core/retire calls Destroy but doesn't affect arcadia.scene's label-registry

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 :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants