Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Bug MissingReferenceException after exiting play mode

Discussion in 'Scripting' started by Joggla, Sep 22, 2023.

  1. Joggla

    Joggla

    Joined:
    Dec 2, 2019
    Posts:
    88
    The game I am working on works fine in playmode. No errors during playing.
    But after I exit play mode I get this error:

    MissingReferenceException: The object of type 'FieldOfView' has been destroyed but you are still trying to access it.
    Your script should either check if it is null or you should not destroy the object.
    FieldOfView.HandleNPCDestroyed (UnityEngine.GameObject destroyedNPC) (at Assets/Scripts/FieldOfView.cs:90)
    NPC.OnDestroy () (at Assets/Scripts/NPC.cs:34)

    As I said all works fine and I only get this error after exiting play mode. It is just anoying and I would like to get rid of the error.
    FieldOfView Component thats attached to the NPC never gets destroyed, disabled or anything like that.
     
  2. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    4,449
    Does it respawn, is it reset at some point during play mode?
     
  3. Joggla

    Joggla

    Joined:
    Dec 2, 2019
    Posts:
    88
    if you have time pls look over it, would be much appreciated.
    this is the npc:


    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using System.Linq;
    6. using Unity.VisualScripting;
    7. using UnityEngine.AI;
    8. using TMPro;
    9.  
    10. public class NPC : MonoBehaviour
    11. {
    12.     public Resources resources = new();
    13.     public Emotions emotions = new();
    14.     public List<AIActionBase> AIActionsBases = new();
    15.     public BrainDatabase brainDatabase = new();
    16.     public TextMeshProUGUI thinkingText;
    17.  
    18.     public ColorGroup colorGroup;
    19.  
    20.     public enum LifeState
    21.     {
    22.         ON,
    23.         OFF,
    24.         DEAD
    25.     }
    26.     public LifeState lifeState;
    27.     public event Action<GameObject> OnNPCDestroyed;
    28.     public NavMeshAgent agent;
    29.  
    30.     private void OnDestroy()
    31.     {
    32.         OnNPCDestroyed?.Invoke(this.gameObject);
    33.     }
    34.  
    35.  
    36.     private void Start()
    37.     {
    38.         agent = GetComponent<NavMeshAgent>();
    39.         AddAIActions();
    40.         InvokeRepeating(nameof(EnergyTick), 0, 1); //starts energy decline
    41.         InvokeRepeating(nameof(MakeADecision), 0, 0.2f);
    42.     }
    43.  
    44.     public void MakeADecision()
    45.     {
    46.         foreach (AIActionBase action in AIActionsBases)
    47.         {
    48.             action.UpdateScore();
    49.         }
    50.         ChooseAction();
    51.     }
    52.  
    53.     public void ChooseAction()
    54.     {
    55.         AIActionsBases = AIActionsBases.OrderByDescending(x => x.score).ToList();
    56.         AIActionsBases.FirstOrDefault().enabled = true;
    57.         thinkingText.text = AIActionsBases.FirstOrDefault().actionName;
    58.         foreach (AIActionBase action in AIActionsBases.Skip(1))
    59.         {
    60.             action.enabled = false;
    61.         }
    62.     }
    63.  
    64.     private void AddAIActions()
    65.     {
    66.         AIActionsBases.Add(this.AddComponent<WanderingAI>());
    67.         AIActionsBases.Add(this.AddComponent<GatherEnergyAI>());
    68.         foreach (AIActionBase aIActionBase in AIActionsBases)
    69.         {
    70.             aIActionBase.enabled = false;
    71.         }
    72.     }
    73.  
    74.     private void EnergyTick()
    75.     {
    76.         if (lifeState == LifeState.ON)
    77.         {
    78.             resources.energy -= 1;
    79.             if (resources.energy <= 0)
    80.             {
    81.                 resources.energy = 0;
    82.                 lifeState = LifeState.OFF;
    83.             }
    84.         }
    85.         else if (lifeState == LifeState.OFF)
    86.         {
    87.             if (resources.energy > 0)
    88.             {
    89.                 lifeState = LifeState.ON;
    90.             }
    91.         }
    92.     }
    93. }
    94.  
    this is the FieldOfView:

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using System.Linq;
    6. using static UnityEngine.GraphicsBuffer;
    7.  
    8. public class FieldOfView : MonoBehaviour
    9. {
    10.     public float radius;
    11.     public float angle;
    12.  
    13.     public LayerMask targetMask;
    14.     public LayerMask obstructionMask;
    15.  
    16.     public NPC npc;
    17.  
    18.     private void Start()
    19.     {
    20.         npc = GetComponent<NPC>();
    21.         StartCoroutine(FOVRoutine());
    22.     }
    23.  
    24.     private IEnumerator FOVRoutine()
    25.     {
    26.         WaitForSeconds wait = new(0.2f);
    27.         while (true)
    28.         {
    29.             yield return wait;
    30.             FieldOfViewCheck();
    31.         }
    32.     }
    33.  
    34.     private void FieldOfViewCheck()
    35.     {
    36.         Collider[] rangeChecks = Physics.OverlapSphere(transform.position, radius, targetMask);
    37.         if (rangeChecks.Length > 0)
    38.         {
    39.             foreach (Collider c in rangeChecks)
    40.             {
    41.                 Transform target = c.transform;
    42.                 Vector3 directionToTarget = (target.position - transform.position).normalized;
    43.                 if (Vector3.Angle(transform.forward, directionToTarget) < angle / 2)
    44.                 {
    45.                     float distanceToTarget = Vector3.Distance(transform.position, target.position);
    46.                     if (!Physics.Raycast(transform.position, directionToTarget, distanceToTarget, obstructionMask))
    47.                     {
    48.                         if (target.gameObject.layer == LayerMask.NameToLayer("NPC"))
    49.                         {
    50.                             if (target.gameObject != this.gameObject)
    51.                             {
    52.                                 npc.brainDatabase.npcs.Remove(npc.brainDatabase.npcs.Where(x => x.gameObject == target.gameObject).FirstOrDefault());
    53.                                 npc.brainDatabase.npcs.Add(new BrainData { gameObject = target.gameObject, lastSeenPosition = target.transform });
    54.                                 target.gameObject.GetComponent<NPC>().OnNPCDestroyed += HandleNPCDestroyed;
    55.                             }
    56.                         }
    57.                         else
    58.                         {
    59.                             npc.brainDatabase.objects.Remove(npc.brainDatabase.objects.Where(x => x.gameObject == target.gameObject).FirstOrDefault());
    60.                             npc.brainDatabase.objects.Add(new BrainData { gameObject = target.gameObject, lastSeenPosition = target.transform });
    61.                             target.gameObject.GetComponent<ObjectBaseComponent>().OnObjectDestroyed += HandleObjectDestroyed;
    62.                         }
    63.                     }
    64.                 }
    65.             }
    66.         }
    67.     }
    68.  
    69.     private void HandleObjectDestroyed(GameObject destroyedObject)
    70.     {
    71.         npc.brainDatabase.objects.Remove(npc.brainDatabase.objects.Where(x => x.gameObject == destroyedObject).FirstOrDefault());
    72.         Searching.SetNewWalkPosition(this.transform, Searching.wanderRadius, npc.agent);
    73.     }
    74.  
    75.     private void HandleNPCDestroyed(GameObject destroyedNPC)
    76.     {
    77.         npc.brainDatabase.npcs.Remove(npc.brainDatabase.npcs.Where(x => x.gameObject == destroyedNPC).FirstOrDefault());
    78.         Searching.SetNewWalkPosition(this.transform, Searching.wanderRadius, npc.agent);
    79.     }
    80. }
    81.  
    the error lines are not correct anymore: MissingReferenceException: The object of type 'FieldOfView' has been destroyed but you are still trying to access it.
    Your script should either check if it is null or you should not destroy the object.
    FieldOfView.HandleNPCDestroyed (UnityEngine.GameObject destroyedNPC) (at Assets/Scripts/FieldOfView.cs:90)
    NPC.OnDestroy () (at Assets/Scripts/NPC.cs:34)

    aka line 90 and 34, since i removed a whole lot of comments
     
  4. Joggla

    Joggla

    Joined:
    Dec 2, 2019
    Posts:
    88
    the updated error with the correct lines:
    MissingReferenceException: The object of type 'FieldOfView' has been destroyed but you are still trying to access it.
    Your script should either check if it is null or you should not destroy the object.
    FieldOfView.HandleNPCDestroyed (UnityEngine.GameObject destroyedNPC) (at Assets/Scripts/FieldOfView.cs:78)
    NPC.OnDestroy () (at Assets/Scripts/NPC.cs:32)
     
  5. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,468
    In the end, this is you asking for this method to be called on an object that has been destroyed. If you look at your code, you're subscribing to this method on all sorts of other objects and I don't see anything that unsubscribes nor anything that would ensure that these objects that are going to call this method, don't do so when they are destroyed; before this "FieldOfView" object is destroyed.

    It just sounds like you're hooking things up and not bothering to unhook them so you're causing an order dependency upon shutdown.

    Not true. What about when...
    This will destroy it.
     
    Joggla and Kurt-Dekker like this.
  6. Joggla

    Joggla

    Joined:
    Dec 2, 2019
    Posts:
    88
    OK thanks, didnt know exiting play mode would call onDestroy on all gameobjects.
    So I unsubscribed in the OnDestroy method and all is good again:

    Code (CSharp):
    1. private void OnDestroy()
    2.     {
    3.         foreach (BrainData npcData in npc.brainDatabase.npcs)
    4.         {
    5.             npcData.gameObject.GetComponent<NPC>().OnNPCDestroyed -= HandleNPCDestroyed;
    6.         }
    7.         foreach (BrainData objectData in npc.brainDatabase.objects)
    8.         {
    9.             objectData.gameObject.GetComponent<ObjectBaseComponent>().OnObjectDestroyed -= HandleObjectDestroyed;
    10.         }
    11.     }
     
    MelvMay likes this.
  7. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    10,468
    Not so much about calling OnDestroy but it actually destroys all objects in the scene as the scene is unloaded as you'd expect. The Editor wouldn't leak those objects. :)