0

I'm trying to learn how to count enemies and remove them from a list if they die. so i have this script which essentially counts the amount of enemies at void start() the issue i'm having though is: when i call the method from a script which removes an enemy from the list i get a Null reference exception error. this is the method i'm trying to call

public void KilledEnemy(GameObject Enemy)
{
    if (Enemies.Contains(Enemy))
    {
        Enemies.Remove(Enemy);
    }

    Debug.Log(Enemies.Count + " Enemies Left");

}

for clarification this is the code with the list.

public class Level1Control : MonoBehaviour
{

    public GameObject Exit;

    public List<GameObject> Enemies = new List<GameObject>();

    public static Level1Control instance;

    // Start is called before the first frame update
    void Start()
    {
        Enemies.AddRange(GameObject.FindGameObjectsWithTag("Enemy"));
        Debug.Log(Enemies.Count + " Enemies Left");
    }

    public void KilledEnemy(GameObject Enemy)
    {
        if (Enemies.Contains(Enemy))
        {
            Enemies.Remove(Enemy);
        }

        Debug.Log(Enemies.Count + " Enemies Left");

    }


    public bool AreOpponentsDead()
    {
        if (Enemies.Count <= 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    private void Update()
    {
        AreOpponentsDead();
        if (AreOpponentsDead() == true)
        {
            OpenDoor();
            Debug.Log("Dead");
        }


    }

    public void OpenDoor()
    {

    }
}

the way i attempt to call the function is like this (this is the entirety of the code in this script:

 public GameObject thisEnemy;

private void OnCollisionEnter(Collision collision)
    {
        if(collision.gameObject.CompareTag("Bullet"))
        {
            Level1Control.instance.KilledEnemy(thisEnemy);
            this.gameObject.SetActive(false);
        }
    }

any help is greatly appreciated.

Edit

ok so thanks to the wonderful people who took the time out of their day to help me the problem was solved!

i added this to my level1control script:

private void Awake()
    {
        instance = this;
    }
  • 1
    Do you ever set the value of Level1Control.instance? – Paramecium13 Nov 09 '20 at 21:51
  • @Paramecium13 the instance variable is there so i can call the script. if thats what you meant? sorry i'm very new to unity and programming in general. – Haroun Ahmad Nov 09 '20 at 21:54
  • 2
    Does this answer your question? [What is a NullReferenceException, and how do I fix it?](https://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-and-how-do-i-fix-it) – Ruzihm Nov 09 '20 at 22:07
  • 1
    This might also be of interest: [Unity singleton manager classes](https://stackoverflow.com/questions/13730112/unity-singleton-manager-classes), although it would be unconventional design to have a singleton for each level, judging by the name `Level1Control`. – Ruzihm Nov 09 '20 at 22:12
  • @Ruzihm Sadly, no the problem i have is that i pass over a GameObject which is defined. the part i don't understand is that it basically doesn't "accept" my GameObject even though it is looking for a GameObject – Haroun Ahmad Nov 09 '20 at 22:13
  • 1
    The question does not say which line produces the null reference exception but I can infer it's on the line `Level1Control.instance.KilledEnemy(thisEnemy);` and `Level1Control.instance` is `null`. – Ruzihm Nov 09 '20 at 22:15
  • @Ruzihm Thank you very much in helping me! – Haroun Ahmad Nov 09 '20 at 22:19
  • My God, the link to "unity singletons" (What? You can't have a singleton in an ECS system.) is one of the most famously bad 10 yr old answers on the site. Pls do not look at that @HarounAhmad !! – Fattie Nov 09 '20 at 22:29
  • @Fattie I don't think it's out of the question to teach newcomers to unity to track a single instance of a component in a static field. After all, the [engineers who make unity consider it reasonable](https://learn.unity.com/tutorial/level-generation?projectId=5c514a00edbc2a0020694718#5c7f8528edbc2a002053b6f7). If the contention is over calling it a "singleton", then that's fair. – Ruzihm Nov 09 '20 at 23:24
  • @Fattie Even the [unity documentation](https://docs.unity3d.com/ScriptReference/Object.FindObjectsOfType.html) recommends in most cases what they call a "singleton pattern" which is probably referring to this pseudo-singleton pattern :). – Ruzihm Nov 09 '20 at 23:40
  • (Unity's "game manager" example there is incredibly poor code, only meant to be taken in a "hello world" sense.) – Fattie Nov 10 '20 at 21:14

2 Answers2

1

It appears that you never assign a value to Level1Control.instance, as such, anytime you try to call a method on it, it will throw an exception.

To fix this, I recommend that you create a constructor for Level1Control that contains instance = this;. Alternatively, you could put that line in the Start method.

Paramecium13
  • 314
  • 1
  • 7
  • Thank you very much! – Haroun Ahmad Nov 09 '20 at 22:18
  • 1
    Ouuwwwwwwch. Certainly this "sort of works" but it will almost certainly fail instantly and catastrophically :) At worst @HarounAhmad just add an "ordinary" global to do this. (So, just a class which >>> IS NOT <<< a component!!! A simple two-line c# class.) – Fattie Nov 09 '20 at 22:30
  • @Fattie yeah, this isn't a good practice. I'd probably do something like have the enemy class look at the current level entity and see if it has a Level1Control component or maybe have the enemy publish an event that the Level1Control can listen to. But I don't know enough about Unity to say how to do that. – Paramecium13 Nov 10 '20 at 19:23
  • fortunately scoring etc. is dead easy, you just need a (say) Scoring monobehavior, which there is only one of (and of course it is DDOL). this is 1 line of code and two clicks, you just have to know the usual scene idiom in a game engine such as unity. https://stackoverflow.com/a/35891919/294884 – Fattie Nov 10 '20 at 19:34
1

As a general rule, you should DEFINITELY NOT be using statics in Unity (unless you are really expert).

It's almost always really meaningless to use a static in a game object environment.

On top of that, the idea of using a static to "point to something similar to an instance of a component using itself" is really, really problematic in Unity.

(Again, if you're a lights-out Unity expert you may do something like that perhaps just during development, but it's really not the way to go!)

Note that, you may just want to have "a global" ..

... note that that WOULD NOT !!! be a component / monobehavior !!! ...

which you could use.

(Of course, just in general in programming it usually sucks to use globals. But it is far "less of a problem" than using a static on a component in an ECS system!!!)

Hence,

public class Settings
{
    public static int buildNumber = 302;
    public static bool quickDevOption = false;
    public static int quickHitCountMap4 = 3;
}

Note that it IS NOT a component / monobehavior!!

It is vanilla c# and has NO CONNECTION to Unity's frame-based, scene-based, system

Please note that what you really want is an ordinary old preload scene, like a manager. This is perfectly easy, one line of code:

https://stackoverflow.com/a/35891919/294884

I strongly encourage you to remove that static from the MonoBehavior!

Fattie
  • 30,632
  • 54
  • 336
  • 607