I don't know where to start... I NEED COMPREHENSIVE HELP!

I’ve got a handful amount of errors to fix, and I don’t know where to start.
Imgur
List of erros in full detail:

Script error: OnTriggerEnter2D
This message parameter has to be of type: Collider2D
The message will be ignored.
NullReferenceException: Object reference not set to an instance of an object
EnemySpawner+<SpawnAllEnemiesInWaves>d__5.MoveNext () (at Assets/Scripts/Enemy Scripts/EnemySpawner.cs:67)
UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) (at <a979f18d51af41179d12b797e8c5be14>:0)
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
<SpawnAllWaves>d__4:MoveNext() (at Assets/Scripts/Enemy Scripts/EnemySpawner.cs:45)
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
<Start>d__3:MoveNext() (at Assets/Scripts/Enemy Scripts/EnemySpawner.cs:31)
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
NullReferenceException: Object reference not set to an instance of an object
EnemySpawner+<SpawnAllEnemiesInWaves>d__5.MoveNext () (at Assets/Scripts/Enemy Scripts/EnemySpawner.cs:67)
UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) (at <a979f18d51af41179d12b797e8c5be14>:0)
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
<SpawnAllWaves>d__4:MoveNext() (at Assets/Scripts/Enemy Scripts/EnemySpawner.cs:45)
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)

EnemyPath.cs source code:

/*****
 * EnemyPath.cs
 * Created by Phaxtolgia
 * 
 * Summary: 1 of 4 scripts that determine how the enemy ships behave.
 * This script instructs the ship to spawn at the first waypoint, assigned via Unity,
 * then despawns once it reaches the last checkpoint.
 *****/

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyPath : MonoBehaviour
{
    //Pro tip to self(owner): "List<type> (variable name)" is NOT an array!

    //Config Parameters (Values modified in Unity.)
    [SerializeField] bool ToggleDebugMode = false;

    //Variable
    List<Transform> waypoints;
    EnemyProp myEnemyProp;
    WaveConfig myWaveConfig;

    int waypointIndex = 0;

    // Start is called before the first frame update
    void Start()
    {
        //This retrieves a set of locations for the enemy ship to move to from WaveConfig.cs.
        waypoints = myWaveConfig.GetWaypoints();
        //Retrieves the 1st waypoint's coordinates, and relocates the enemy ship
        //To the 1st waypoint.
        transform.position = waypoints[waypointIndex].transform.position;
    }

    // Update is called once per frame
    void Update()
    {
        //Author's note: I might need coroutine, depending on how I want the ship to move in the future.
        //For now Coroutine & IEnumerators are commented out.
        //StartCoroutine(MoveCor());
        Move();
    }

    /*****
     * SET WAVE CONFIG
     * 
     * Takes in whatever WaveConfig was inserted into this method and makes myWaveConfig in this script
     * equal to the inserted WaveConfig in the reference.
     *****/
    public void SetWaveConfig(WaveConfig myWaveConfig)
    {
        this.myWaveConfig = myWaveConfig;
    }

    /*****
     * MOVE
     * 
     * Enemy ship moves to assigned waypoints' coordinates.
     * 
     * targetPos is assigned to the waypoint's coordinates, relaying where the ship should move to.
     * movementThisFrame is assigned to  * Time.deltaTime (again, to ensure it moves consistently,)
     * 
     * This ship's position is made equal to Vector2.MoveTowards(Vector2 current, Vector2 target, float)
     * -"Vector2 current" should be the object's current position, so ideally it's always "transform.position",
     * -"Vector2 target" is where the object is assigned to move to.
     * -"float" variable is the object's assigned speed. It'll never go faster than this float variable.
     * 
     * The if & else statements check if the ship is at one of the waypoint's coordinates.
     * -If it is, increment waypointIndex, thus telling the ship to move to the next waypoint.
     * -Else, destroy itself once all waypoints have been visited.
     * 
     * TODO: Add a function that allows it to operate even without waypoints, and it'll despawn once offscreen to the left.
     *****/
    private void Move()
    {
        //Arrays use ".length", and Lists use ".Count". 
        if (waypointIndex <= waypoints.Count - 1)
        {
            var targetPos = waypoints[waypointIndex].transform.position;
            //Temporarily changed moveSpeed to waveConfig for lecture.
            var movementThisFrame = myEnemyProp.GetMoveSpeed() * Time.deltaTime;

            transform.position = Vector2.MoveTowards
                (transform.position, targetPos, movementThisFrame);
            if (transform.position == targetPos)
            {
                waypointIndex++;
            }
        }
        else
        {
            Destroy(gameObject);
        }
    }

    /*
    IEnumerator MoveCor()
    {
        //Arrays use ".length", and Lists use ".Count". 
        if (waypointIndex < waypoints.Count)
        {
            var targetPos = waypoints[waypointIndex].transform.position;
            var movementThisFrame = moveSpeed * Time.deltaTime;

            transform.position = Vector2.MoveTowards
                (transform.position, targetPos, movementThisFrame);
            if (transform.position == targetPos)
            {
                waypointIndex++;
                yield return new WaitForSeconds(2f);
            }
            else
            {
                Destroy(gameObject);
            }

            //DEBUG
            if (ToggleDebugMode)
            {
                Debug.Log(targetPos + ", " + movementThisFrame);
            }
        }
        //yield return null;
    }*/
}

WaveConfig.cs source code:

/*****
 * WaveConfig.cs
 * Created by Phaxtolgia
 * 
 * Summary: 2 of 4 scripts that determine how the enemy ships behave.
 * Creates a new type of file within Unity that stores properties about waves, including what kind of enemy the wave will include,
 * #The path they will take,# how frequently the ship spawns (timeBetweenSpawns), deviation for how frequently the ship spawns
 * (spawnRandomFactor), and how many enemy ships will spawn.
 *****/

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(menuName = "Enemy Wave Config")]
public class WaveConfig : ScriptableObject
{
    //Config Parameters
    [SerializeField] GameObject enemyPrefab;
    [SerializeField] GameObject pathPrefab;
    [SerializeField] float timeBetweenSpawns = 0.5f;
    [SerializeField] float spawnRandomFactor = 0.3f;
    [SerializeField] int enemyQuantity = 5;

    /*****
     * GET "X" PREFAB
     * 
     * Retrieves the respective prefab from Config Parameters.
     *****/
    public GameObject GetEnemyPrefab() { return enemyPrefab; }
    public float GetTimeBetweenSpawns() { return timeBetweenSpawns; }
    public float GetSpawnRandomFactor() { return spawnRandomFactor; }
    public int GetEnemyQuantity() { return enemyQuantity; }

    /*****
     * GET WAYPOINTS
     * 
     * TBD
     *****/
    public List<Transform> GetWaypoints()
    {
        var waveWaypoints = new List<Transform>();
        foreach(Transform child in pathPrefab.transform)
        {
            waveWaypoints.Add(child);
        }

        return waveWaypoints;
    }
}

EnemySpawner.cs source code:

/*****
 * EnemySpawner.cs
 * Created by Phaxtolgia
 * 
 * Summary: 3 of 4 scripts that determine how the enemy ships behave.
 * Dictates what waves will spawn in the game, and whether the waves are repeated or not via Unity.
 *****/

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemySpawner : MonoBehaviour
{
    //Config Parameters
    [SerializeField] List<WaveConfig> waveConfigs;
    [SerializeField] bool looping = false;
    
    //Variable
    int startingWave = 0;

    /*****
     * START
     * 
     * Start is called before the first frame update. Being an IEnumerator allows the waves to repeat indefinitely.
     *****/
    IEnumerator Start()
    {
        do
        {
            yield return StartCoroutine(SpawnAllWaves());
        }
        while (looping);
    }
    /*****
     * SPAWN ALL WAVES
     * 
     * Spawns all the waves registered within the script via Unity.
     *****/
    private IEnumerator SpawnAllWaves()
    {
        for (int waveIndex = startingWave; waveIndex < waveConfigs.Count; waveIndex++)
        {
            var currentWave = waveConfigs[waveIndex];
            yield return StartCoroutine(SpawnAllEnemiesInWaves(currentWave));
        }
    }

    /*****
     * SPAWN ALL ENEMIES IN WAVES
     * 
     * Spawns multiple enemies depending on EnemyQuantity in waveConfig.
     * 
     * newEnemy is where the enemies are going to spawn. "var newEnemy = x" is normally optional,
     * but to give ships info about where to go and how quickly or slowly to move,
     * {newEnemy.GetComponent<EnemyPath>().SetWaveConfig(waveConfig)} is necessary to pass to EnemyPath.cs.
     *****/
    private IEnumerator SpawnAllEnemiesInWaves(WaveConfig waveConfig)
    {
        for (int waveEnemyCount = 0; waveEnemyCount < waveConfig.GetEnemyQuantity(); waveEnemyCount++)
        {
            var newEnemy = Instantiate(
                waveConfig.GetEnemyPrefab(),
                waveConfig.GetWaypoints()[0].transform.position,
                Quaternion.identity);
            Debug.Log(waveEnemyCount);
            newEnemy.GetComponent<EnemyPath>().SetWaveConfig(waveConfig);

            yield return new WaitForSeconds(waveConfig.GetTimeBetweenSpawns());
        }
        
    }
}

EnemyProp.cs source code:

/*****
 * EnemyProp.cs
 * (Prop stands for Properties.)
 * Created by Phaxtolgia
 * 
 * Summary: 4 of 4 scripts that determine how the enemy ships behave.
 * Stores various property about the enemy ship, such as their HP, attack, etc.
 *****/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyProp : MonoBehaviour
{
    [SerializeField] float health = 300f;
    [SerializeField] float currentHealth = 0f; //DEBUG
    [SerializeField] float moveSpeed = 5f;

    // Start is called before the first frame update
    void Start()
    {
        currentHealth = health;
    }

    // Update is called once per frame
    void Update()
    {
        if (currentHealth <= 0)
        {
            Destroy(gameObject);
        }
    }

    public float GetMoveSpeed() { return moveSpeed; }

    /*****
     * On Trigger Enter 2D
     * 
     * Activates when something enters the attached object with this component.
     *****/
//Wtf. This is exactly as the lecturer typed. Why is it freaking out?
    private void OnTriggerEnter2D(Collision2D other)
    {
        AttackProp myAttackProp = other.gameObject.GetComponent<AttackProp>();
        currentHealth -= myAttackProp.GetDamage();
    }
}

Hi,

Have you already compared your code to the Lecture Project Changes which can be found in the Resources of this lecture? Since the error message mentioned the OnTriggerEnter2D method, that’s something you could check first.

NullReferenceException means that a reference (“link”) to an instance is missing. Double click on the error message to see to which line in your code it is referring. If you exposed a field in the Inspector, make sure that it’s not empty.

Hopefully, this helped. :slight_smile:

1 Like

Code: Says “Collider2D.”
Me: Reads “Collision2D.”

FML. I really need to look for stuff like this. They’re causing way more headache than they deserve.

As for the rest of the code, I’ve yet to understand what’s going on, because there are no empty fields in my inspector. At least it’s a start.

If we take your next error as an example:

NullReferenceException almost always means you tried to call a method on an object, but that object wasn’t created/ready/filled and is instead null.

It then gives you a clue (actually precise information) about where the problem occurred - line 67 of your EnemySpawner script; that’s what this means: EnemySpawner.cs:67

So let’s look at that line:

    private IEnumerator SpawnAllEnemiesInWaves(WaveConfig waveConfig)
    {
        for (int waveEnemyCount = 0; waveEnemyCount < waveConfig.GetEnemyQuantity(); waveEnemyCount++)
        {
            var newEnemy = Instantiate(
                waveConfig.GetEnemyPrefab(),
                waveConfig.GetWaypoints()[0].transform.position,
                Quaternion.identity);
            Debug.Log(waveEnemyCount);
            newEnemy.GetComponent<EnemyPath>().SetWaveConfig(waveConfig); // this is line 67

            yield return new WaitForSeconds(waveConfig.GetTimeBetweenSpawns());
        }
        
    }

I took the liberty of adding a comment where line 67 is, because the other lines are good for context here.

Starting from the left, we have newEnemy - if this was null, it would be the cause because an attempt to call GetComponent<>() on null would throw that null reference exception.

But we pretty much know that’s not going to be the case unless waveConfig.GetEnemyPrefab() itself returns null when you’re using it in your Instantiate() call.

You should check, just in case. A Debug.Log(waveConfig.getEnemyPrefab()); just before line 67 should be sufficient.

If that is not null, we turn our attention to the next thing in line, which is the result of .GetComponent<EnemyPath>(). You can again check with a Debug.Log(newEnemy.GetComponent<EnemyPath>()); call before line 67 to check, or else go through each of the prefabs your waveconfig can have assigned and make sure they do all have that component on them.

After that we have the end of the line with .SetWaveConfig(waveConfig); and we can pretty much forget about this - if we’ve made it that far, the only place a null reference will then occur is INSIDE the SetWaveConfig() method, and the error message clearly says we hit the error on line 67 of EnemySpawner before we got that far.

So your problem is absolutely one of these two things being null: either newEnemy itself, because waveConfig.GetEnemyPrefab() didn’t return anything earlier, or newEnemy.GetComponent<EnemyPath>() because what it did return doesn’t have an EnemyPath component on it.

Hope that helps in tracking down your next issue in the series.

I should’ve seen this sooner, but I found my problem: I removed the EnemyPath component from my enemy ships, causing it to freak out so much.

Only reason I did this is because the lecturer had the opposite problem that I did, and removed the EnemyPath component to fix the problem. Strange how I got the inverse result.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.