1
votes

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 somethime give 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 wich 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.

4
Can u post the whole stack trace ?Z3RP
Well, you've got a lot of jacked up logic. In your Update(), if check, swap IsInRange() and attacker != null. If not, you're going into IsInRange() with a null attacker (Maybe). Now, you can remove your attacker null checks in IsInRange() which could've made your foreach loop pop. Also, currentAttacker is not going to be null, it just won't be in your list. You need to maintain a flagjiveturkey
Thanks a lot for finding out the error. Thank you, game development is the only thing I want to do in this life. Without the help of you guys I can't do that. Because it is really a bit difficult to do everything by yourself. Thank you .user5234003
@jiveturkey Thank you jiveturkey for helping. With your help I am able to make my 3rd game.I have mention you name in description hope you don't mind. lifeisjourney.itch.io/snowmanuser5234003
Thanks much for the kudos.jiveturkey

4 Answers

5
votes

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);
2
votes

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

1
votes

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

0
votes
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
                }
        }