Course Realm Rush - NullReferenceException during change of original Code

Hi together,

i have completed the course and started again with an own project.

Cause of the typ of game the enemies will have to be put randomly together as in the course BUT they have their own startCoordinates each in the Enemy Script.

Everything works but it keeps calling:

NullReferenceException: Object reference not set to an instance of an object
Pathfinder.GetNewPath (UnityEngine.Vector2Int coordinates) (at Assets/Game/Pathfinding/Pathfinder.cs:55)
EnemyMover.RecalculatePath (System.Boolean resetPath) (at Assets/Game/Scripts/EnemyMover.cs:49)
EnemyMover.OnEnable () (at Assets/Game/Scripts/EnemyMover.cs:19)
UnityEngine.Object:Instantiate(GameObject, Transform)
ObjectPool:PopulatePool() (at Assets/Game/Scripts/ObjectPool.cs:38)
ObjectPool:Awake() (at Assets/Game/Scripts/ObjectPool.cs:18)

From my pov i have to search for an not assigned GameObject and have added Debug.Log´s in the ObjectPool Script before and after Line 18:

using System;
using System.Collections;
using UnityEngine;

public class ObjectPool : MonoBehaviour
{
    [SerializeField] GameObject[] enemyTyps;
    [SerializeField] [Range(0, 50)] int poolSize = 10;
    [SerializeField] [Range(0.1f, 30f)] float spawnTimer = 1f;

    GameObject enemy;
    GameObject[] pool = new GameObject[0];

    void Awake()
    {        
        PopulatePool();
    }


    void Start()
    {
        StartCoroutine(SpawnEnemy());

    }

    void PopulatePool()
    {
        pool = new GameObject[poolSize];
        for (int i = 0; i < poolSize; i++)
        {            
            enemy = enemyTyps[UnityEngine.Random.Range(0, enemyTyps.Length)];
            Debug.Log(enemy);
            Debug.Log(pool[i]);
            Debug.Log(this);
            
            pool[i] = Instantiate(enemy,transform);
            pool[i].SetActive(false);

            Debug.Log(enemy);
            Debug.Log(pool[i]);
            Debug.Log(this);
        }
    }

    void EnableObjectInPool()
    {
        for(int i = 0; i < pool.Length; i++)
        {            
            if(!pool[i].activeInHierarchy)
            {
                pool[i].SetActive(true);
                return;
            }

        }
    }

    IEnumerator SpawnEnemy()
    {        
        while (true)
        {
            EnableObjectInPool();
            yield return new WaitForSeconds(spawnTimer);                           
        }     
    }
}

The Only Object wich is null before the instantiation is pool[i]

Now i don´t have a clue how to solve it cause this pool array is empty before its filled during Awake() within the PopulatePool() function.

I hope anybody can follow my case and is able to help.

Does the other references to the other scripts matter for this NullRef Error? Or am I wrong and the issue is maybe elsewhere? I hadn´t offen the fact that there where so many references to code in an NullRef so i m just a lil bit kicked back.

pathfinder.cs

public class Pathfinder : MonoBehaviour
{
    Vector2Int startCoords;
    public Vector2Int StartCoordinates { get { return startCoords; } }
    [SerializeField] Vector2Int destinationCoords;
    public Vector2Int DestinationCoordinates { get { return destinationCoords; } }

    Node startNode;
    Node destinationNode;
    Node currentSearchNode;

    //Queues  List with FirstIn FirstOut (FIFO)
    Queue<Node> frontier = new Queue<Node>();
    Dictionary<Vector2Int, Node> reached = new Dictionary<Vector2Int, Node>();
    
    Vector2Int[] directions = { Vector2Int.right, Vector2Int.left, Vector2Int.up, Vector2Int.down };
    GridManager gridManager;
    Dictionary<Vector2Int, Node> grid = new Dictionary<Vector2Int, Node>();

    void Awake()
    {        
        startCoords = StartCoordinates;
            Debug.Log(startCoords);
             
        gridManager = FindObjectOfType<GridManager>();
        if (gridManager != null)
        {
            grid = gridManager.Grid;
            startNode = grid[startCoords];
            destinationNode = grid[destinationCoords];            
        }      
    }

    void Start()
    {        
        GetNewPath();
    }

    //Overloading GetNewPath() Method, copy Method and setup parameters ==>Lookat Instantiate and its many possible parameters
    public List<Node> GetNewPath()
    {
        //gridManager.ResetNodes();
        //BreadthFirstSearch(startCoords);
        //return BuildPath();
        // get rid of above and just call new GetNewPath with startCoords parameter
        return GetNewPath(startCoords);
    }

    public List<Node> GetNewPath(Vector2Int coordinates)
    {
        gridManager.ResetNodes();
        BreadthFirstSearch(coordinates);
        return BuildPath();
    }

    void ExploreNeighbors()
    {
        List<Node> neighbors = new List<Node>();

        foreach (Vector2Int direction in directions)
        {
            Vector2Int neighborCoords = currentSearchNode.coordinates + direction;
            //Debug.Log(neighborCoords);

            if (grid.ContainsKey(neighborCoords))
            {
                neighbors.Add(grid[neighborCoords]);                
            }
        }
        foreach (Node neighbor in neighbors)
        {
            if(!reached.ContainsKey(neighbor.coordinates) && neighbor.isWalkable)
            {
                neighbor.connectedTo = currentSearchNode;
                reached.Add(neighbor.coordinates, neighbor);
                frontier.Enqueue(neighbor);
            }
        }
    }

    void BreadthFirstSearch(Vector2Int coordinates)
    {
        startNode.isWalkable = true;
        destinationNode.isWalkable = true;
        frontier.Clear();
        reached.Clear();
        
        bool isRunning = true;

        frontier.Enqueue(grid[coordinates]);
        reached.Add(coordinates, grid[coordinates]);

        while(frontier.Count > 0 && isRunning)
        {
            currentSearchNode = frontier.Dequeue();
            currentSearchNode.isExplored = true;
            ExploreNeighbors();
            if (currentSearchNode.coordinates == destinationCoords)
            {
                isRunning = false;
            }
        }
    }

    List<Node> BuildPath()
    {
        List <Node> path = new List<Node>();
        Node currentNode = destinationNode;

        path.Add(currentNode);
        currentNode.isPath = true;

        while(currentNode.connectedTo != null)
        {
            currentNode = currentNode.connectedTo;
            path.Add(currentNode);
            currentNode.isPath = true;
        }
        path.Reverse();

        return path;
    }

    public bool WillBlockPath(Vector2Int coordinates)
    {
        if(grid.ContainsKey(coordinates))
        {
            bool previousState = grid[coordinates].isWalkable;

            grid[coordinates].isWalkable = false;
            List<Node> newPath = GetNewPath();
            grid[coordinates].isWalkable = previousState;

            if(newPath.Count <= 1)
            {
                GetNewPath();
                return true;
            }            
        }
        return false;
    }

    public void NotifyReceivers()
    {
        //calls RecalculatePath() to any listening Gameobjects and its children
        BroadcastMessage("RecalculatePath", false, SendMessageOptions.DontRequireReceiver);
    }
}

enemymover.cs

public class EnemyMover : MonoBehaviour
{    
    [SerializeField][Range(0f, 5f)] float speed = 0.5f;

    List<Node> path = new List<Node>();

    Enemy enemy;
    GridManager gridManager;
    Pathfinder pathfinder;

    void OnEnable()
    {        
        ReturnToStart();
        RecalculatePath(true);                
    }

    void Awake()
    {
        enemy = GetComponent<Enemy>();
        gridManager = FindObjectOfType<GridManager>();
        pathfinder = FindObjectOfType<Pathfinder>();
    }

    void RecalculatePath(bool resetPath)
    {
        Vector2Int coordinates = new Vector2Int();

        if (resetPath)
        {
            if (enemy.StartCoordinates != null)
            {
                coordinates = /*pathfinder.*/enemy.StartCoordinates;
                //Debug.Log(coordinates);
            }
            else coordinates = new Vector2Int(10, 6);
        }
        else
        {
            coordinates = gridManager.GetCoordinatesFromPosition(transform.position);
        }
        StopAllCoroutines();
        path.Clear();
        //Debug.Log(coordinates);
        path = pathfinder.GetNewPath(coordinates);
        StartCoroutine(FollowPath());
    }

    void ReturnToStart()
    {
        transform.position = gridManager.GetPositionFromCoordinates(/*pathfinder.*/enemy.StartCoordinates);
    }

    void FinishPath()
    {
        enemy.StealGold();
        gameObject.SetActive(false);
    }

    IEnumerator FollowPath()
    {
        for(int i = 1; i < path.Count; i++)
        {
            Vector3 startPosition = transform.position;
            Vector3 endPosition = gridManager.GetPositionFromCoordinates(path[i].coordinates);
            float travelPercent = 0f;

            transform.LookAt(endPosition);

            while (travelPercent < 1f)
            {
                travelPercent += Time.deltaTime * speed;
                transform.position = Vector3.Lerp(startPosition, endPosition, travelPercent);
                yield return new WaitForEndOfFrame();
            }
        }

        FinishPath();
    }
}

Many thanks in advance for any help on this issue.

Greets Chris

The error is not in the ObjectPool, it’s in Pathfinder
Pathfinder.GetNewPath (UnityEngine.Vector2Int coordinates) (at Assets/Game/Pathfinding/Pathfinder.cs:55)
From what I can see (and guess) gridManager may be null.

Hi bixarrio,

thanks for the quick answer.

After using Debug.log for the Gridmanager it seems only in the first attempt it is null and within the first second it is as it has to be an Gridmanager is no longer null.
In the Script Execution Order, i call ObjectPool, GridManager, Pathfinder and Tile.

Maybe my approach to carry the startingCoordinates in the enemies prefab killed anything, i don´t know but it was the only idea i had to get individual startingcoords for different types of enemies.

This is the problem. ObjectPool instantiates an enemy, and that enemy tries to determine a path before the GridManager has been initialised. Perhaps move the code in the ObjectPool.Awake() to ObjectPool.OnEnable() or ObjectPool.Start(). It will then give the GridManager time to get initialised before it is being used

bixarrio - Master Problem Solver - many many many thanks 4 your help bro !!!

I just switched PopulatePool() to Start and set the SEO GridManager over ObjectPool and the problem is gone :slight_smile:

Thank you so much

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

Privacy & Terms