user5234003
user5234003

Reputation: 77

MissingReferenceException: The object of type 'Attacker' has been destroyed but you are still trying to access it

I am getting the above error and I tried to use debug.log to print where the error is. I am creating a type of tank obj. which fire to some instantiated obj. Instantiated object are attacker.

In update I am using foreach loop to loop through all the instantiated object. If found and If object are in range fire.

void Update () {
        
        if (attacker!= null  )
        {
            //Debug.Log("inside att");
            attacker = FindObjectsOfType<Attacker>();
        }
        
        // fire only if attacker is in range 
        
        if (IfInRange() && attacker!= null && running)
        {
          
            faceTowardTarget();
            Fire();
        }
       
    }
    bool IfInRange()
    {// run through all the instantiated attacker
        foreach (Attacker currentAttacker in attacker)

This works fine but sometimes gives above. At the end in the console the loop goes on and on and currentAttacker is null in the end. I tried to print that in console. but it don't go in other if statement

{   //if attacker is in range
            
            if (attacker != null )
                {
                    
                    Debug.Log(currentAttacker.GetComponent<Health>().isAlive);
                   
                    if (Vector2.Distance(transform.position, currentAttacker.transform.position) < minDistance)               
                    {
                        
                        attackerPos = currentAttacker.transform;
                        
                        return true;
                    }
                }
                if (currentAttacker == null)
                {
                    Debug.Log("curre Attacker null");
                    running = false;
                    return false;
                }
                
             }return false;
        }

Attacker have a simple health script which deal with damage if hit by projectile.

Void update()
    {
     if (health <= 0)
            {
**is there any better way to destroy an obj. If i destroy gameObject that error appear and it get stuck in foreach loop**
               // Destroy(gameObject);
                noOfKilled++;
                isAlive = false;
                scoreHolder.addScore(scoreValue);
            }
    }

Thank you so much for helping. I tried searching but unable to resolve this.

Upvotes: 1

Views: 1030

Answers (4)

Technivorous
Technivorous

Reputation: 1712

I'm going to assume, the error shows up when instantiating another tank after the first tank is destroyed? all your tanks are clones of tank1, so when tank1 is destroyed, you get a null pointer, because its trying to instantiate an object thats not there anymore.

2 solutions...

A) UGLY: change tank1's initial position to somewhere it will never be destroyed, say 5000,5000,5000. since the tank cant be destroyed, it will never be null

B) THE SMART, CORRECT WAY: make a prefab. make a folder called prefabs, drag tank1 into it. select your script which spawns tanks, and drag the PREFAB copy of tank1 into it. now you always have an instance of tank1 and your null pointer is gone!

i cannot stress enough the importance of proper use of prefabs, not just for performance and reliabilty, but sanity as well...

Upvotes: 1

victor dabija
victor dabija

Reputation: 505

FindObjectsOfType<Attacker>(); //returns an array

will return an array, not an object, use attackers[0] for firstone or use

FindObjectOfType<Attacker>()

(will return the first found).

Using objectsOfType you always have an empty array, so

if (attacker==null)

of course will return false because the array isn't null, its just empty. This is why it doesn't go in your second if statement;

Also

void update()
        {
         if (health <= 0)
                {
    **is there any better way to destroy an obj. If i destroy gameObject that error appear and it get stuck in foreach loop**

                    noOfKilled++;
                    isAlive = false;
                    scoreHolder.addScore(scoreValue);
                    Destroy(gameObject);// destroy it after at the end, not before it have something else to do
                }
        }

Upvotes: 0

Programmer
Programmer

Reputation: 125275

The quick and dirty way to fix this is to use the DestroyImmediate function instead of the Destroy function. Using DestroyImmediate(gameObject) will destroy the object in that frame and FindObjectsOfType<Attacker>() cannot find it so it won't be accessed in the foreach loop.


The proper way to this is to create a List in your main code to hold the instantiated Attacker script:

public GameObject attackerPrefab;
public List<Attacker> attacker = new List<Attacker>();

When you instantiate the prefab, add the Attacker script to the List:

//Instantiate
GameObject obj = Instantiate(attackerPrefab);
//Get Attacker component 
Attacker tempAttacker = obj.GetComponent<Attacker>();
//Add Attacker component to List
attacker.Add(tempAttacker);

Finally, when you want to destroy the attacker object in your health script, remove it from the List then destroy it. By removing it from the List before destroying it, you won't be accessing an object marked as destroyed.

//Get the script that stores the List of Attacker
Test otherScript = GameObject.Find("NameOfGameObjectYourScriptIsAttachedTo").GetComponent<Test>();
//Remove from the List
otherScript.attacker.Remove(gameObject.GetComponent<Attacker>());
//Now, destroy this gameObject
Destroy(gameObject);

Upvotes: 5

Z3RP
Z3RP

Reputation: 328

There many options to solve this problem. One Option is don't use a foreach loop when u edit the loop. But when u want to keep the foreach loop just save the objects in a other list and destroy them after the foreach loop.

Example1

for(int i = 0; i < attackers.Count; i++)
            {
                //if in range
                attackers.RemoveAt(i);
                i--;
            }

Example2

List<string> attackers = new List<string>();
        List<string> _shootAt = new List<string>();
        foreach(string attacker in attackers)
        {
            //if in range
            _shootAt.Add(attacker);
        }

        foreach (string attacker in _shootAt)
        {
            //if in range
            attackers.Remove(attacker);
        }

I hope that can help you

Upvotes: 2

Related Questions