Null exception error

I think my pathfinding script is returning the path with only the endpoint in it, so I get an error…

Screen Shot 2020-04-18 at 9.52.12 PM

there’s 15 blocks in the scene but the list looks like this

Screen Shot 2020-04-18 at 9.54.56 PM

here are the scripts mentioned …

PathFinder.cs

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

public class PathFinder : MonoBehaviour
{
    // SHIPPING DATA //
    Vector2Int[] pathDirections = { Vector2Int.up, Vector2Int.right, Vector2Int.down, Vector2Int.left };


    // CHANGING DATA //
    [SerializeField] WaypointBlock startWaypointBlock;
    [SerializeField] WaypointBlock endWaypointBlock;
    [SerializeField] Dictionary<Vector2Int, WaypointBlock> grid = new Dictionary<Vector2Int, WaypointBlock>();
    [SerializeField] List<Transform> childList = new List<Transform>();
    [SerializeField] List<Transform> blocksToRemove = new List<Transform>();
    Queue<WaypointBlock> queue = new Queue<WaypointBlock>();
    [SerializeField] List<WaypointBlock> path = new List<WaypointBlock>(); // TODO make private
    [Header("DO NOT CHANGE - exposed for debugging")]
    [SerializeField] bool isRunning = true; // TODO maybe hide to protect


    // * CODE * //

    // Start is called before the first frame update
    void Start()
    {
        BuildChildList();
        BuildGridDictionary();
        RemoveDuplicateBlocksFromChildList();
        // TODO i want the child list to be sorted alphanumerically so that we
        // could use the child list to find the first and last blocks by grid
        // (and reference those in the dictionary)
        // and make those the start and goal automatically
        ColorStartAndGoalBlocks();
    }

    public List<WaypointBlock> GetPath()
    {
        BreadthFirstSearch();
        CreatePath();
        return path;
    }

    private void CreatePath()
    {
        path.Add(endWaypointBlock);
        WaypointBlock previous = endWaypointBlock.queuedFrom;
        while (previous != startWaypointBlock)
        {
            path.Add(previous);
            previous = previous.queuedFrom;
        }
        path.Add(startWaypointBlock);
        path.Reverse();
    }

    private void BreadthFirstSearch()
    {
        queue.Enqueue(startWaypointBlock);
        while(queue.Count > 0 && isRunning)
        {
            WaypointBlock searchCenter = queue.Dequeue();
            print("Searching from " + searchCenter); // TODO remove later
            HaltIfEndFoundFrom(searchCenter);
            ExploreNeighbors(searchCenter);
            searchCenter.isExplored = true;
        }
        print("Fishined Searching");
    }

    private void HaltIfEndFoundFrom(WaypointBlock searchCenter)
    {
        if (searchCenter == endWaypointBlock)
        {
            print("searching from end point, stopping search"); // TODO remove later
            isRunning = false;
            ColorStartAndGoalBlocks();
        }
    }

    private void ExploreNeighbors(WaypointBlock from)
    {
        if (!isRunning) { return; }
        int i = 0;
        int x = from.GetGridPos().x;
        int y = from.GetGridPos().y;
        foreach (Vector2Int direction in pathDirections)
        {
            Vector2Int nextDirection = new Vector2Int(x + pathDirections[i].x, y + pathDirections[i].y);
            print("Exploring " + nextDirection + " from " + from);
            if (grid.ContainsKey(nextDirection))
            {
                QueueNewNeighbor(nextDirection, from);
            }
            else
            {
                print(nextDirection + " does not exist in the current grid");
            }
            i++;
        }
    }

    private void QueueNewNeighbor(Vector2Int nextDirection, WaypointBlock from)
    {
        if (grid[nextDirection].isExplored || queue.Contains(grid[nextDirection])) 
        {
            print(grid[nextDirection] + " has been explored alreayd");
        }
        else
        {
            grid[nextDirection].SetTopColor(Color.blue); // todo move later?
            queue.Enqueue(grid[nextDirection]);
            grid[nextDirection].queuedFrom = from;
            print("Queueing " + grid[nextDirection] + " from " + from);
        }
    }

    private void ColorStartAndGoalBlocks()
    {
        startWaypointBlock.SetTopColor(Color.green);
        endWaypointBlock.SetTopColor(Color.red);
    }

    private void RemoveDuplicateBlocksFromChildList()
    {
        foreach (Transform duplicate in blocksToRemove)
        {
            childList.Remove(duplicate);
            Debug.LogWarning("removed " + duplicate + " from original childlist");
        }
    }

    private void BuildGridDictionary()
    {
        foreach (Transform gridBlock in childList)
        {
            WaypointBlock waypointBlock = gridBlock.GetComponent<WaypointBlock>();
            bool duplicateWaypoint = grid.ContainsKey(waypointBlock.GetGridPos());
            if (duplicateWaypoint)
            {
                CheckIfDuplicateIsImportant(waypointBlock);
                //Debug.LogWarning("Duplicate block " + waypointBlock);
                waypointBlock.gameObject.SetActive(false);
                blocksToRemove.Add(gridBlock);
                //Debug.LogWarning("added " + gridBlock + " to remove list");
            }
            else
            {
                grid.Add(waypointBlock.GetGridPos(), waypointBlock);
            }
            //print(grid);
            //print(grid.Count);
        }
    }

    private void CheckIfDuplicateIsImportant(WaypointBlock waypointBlock)
    {
        if (waypointBlock == startWaypointBlock)
        {
            startWaypointBlock = grid[waypointBlock.GetGridPos()];
        }
        if (waypointBlock == endWaypointBlock)
        {
            endWaypointBlock = grid[waypointBlock.GetGridPos()];
        }
    }

    private void BuildChildList()
    {
        foreach (Transform child in transform)
        {
            childList.Add(child);
        }
    }
}

and EnemyMovement.cs

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

public class EnemyMovement : MonoBehaviour
{
    // SHIPPING DATA //



    // CHANGING DATA //
    [SerializeField] PathFinder myGrid;

    // * CODE * //

    // Start is called before the first frame update
    void Start()
    {
        //myGrid = GetComponent<PathFinder>();
        List<WaypointBlock> path = myGrid.GetPath();
        StartCoroutine(FollowPath(path));
    }

    IEnumerator FollowPath(List<WaypointBlock> path)
    {
        print("Starting Patrol...");
        foreach (WaypointBlock cube in path)
        {
            transform.position = cube.transform.position;
            print("Visiting Block: " + cube.name);
            yield return new WaitForSeconds(1f);
        }
        print("Ending Patrol...");
    }
}

Hi Heath,

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.

Does your code access Element 1 in your Path array? If so, that’s the problem because that element does not contain any reference to an object. It is empty (null).

that list is the path list, it should have 4 or 5 elements in it, so either the path is being asked to return before it can be built, or this is all happening and returning before the path is even figured out and the enemy is asking for it, receiving it, and trying to access the null element… I need to stall the return somehow… how would I so that in that GetPath() getter… how can I stall it between CreatePath(); and the return?

or even better… instead of a timer how would I put a check in there to not send it until the path creation complete

if I make the path a public list, and move everything that gets the path back into start on the pathfinder script, when the enemy tries to grab the list on start, it starts its patrol with an empty list, the list builds…

If I leave that structure and change the start method in pathfinder to an awake method, then when the emend starts and asks for the public path list, it retrieves it and makes it’s way to the endpoint, confetti guns blow, crowds cheer, flowers bloom… it’s great…

that confirms to me that it’s taking longer to build the path than the enemy is retrieving it if it calls it on start…

Ok… I changed the start method on my pathfinder to awake, getting the blocks listed, in the dictionary, disabling duplicates, doing the duplicate check in the start or end waypoints, getting it all ready before the enemy can hit it’s start method.

then the enemy can properly call for the path on start, and everything works in that timeframe… here are the corrected scripts…

PathFinder.cs

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

public class PathFinder : MonoBehaviour
{
    // SHIPPING DATA //
    Vector2Int[] pathDirections = { Vector2Int.up, Vector2Int.right, Vector2Int.down, Vector2Int.left };


    // CHANGING DATA //
    [SerializeField] WaypointBlock startWaypointBlock;
    [SerializeField] WaypointBlock endWaypointBlock;
    [SerializeField] Dictionary<Vector2Int, WaypointBlock> grid = new Dictionary<Vector2Int, WaypointBlock>();
    [SerializeField] List<Transform> childList = new List<Transform>();
    [SerializeField] List<Transform> blocksToRemove = new List<Transform>();
    Queue<WaypointBlock> queue = new Queue<WaypointBlock>();
    [SerializeField] List<WaypointBlock> path = new List<WaypointBlock>();
    [Header("DO NOT CHANGE - exposed for debugging")]
    [SerializeField] bool isRunning = true; // TODO maybe hide to protect


    // * CODE * //

    // Start is called before the first frame update
    void Awake()
    {
        BuildChildList();
        BuildGridDictionary();
        RemoveDuplicateBlocksFromChildList();
        // TODO i want the child list to be sorted alphanumerically so that we
        // could use the child list to find the first and last blocks by grid
        // (and reference those in the dictionary)
        // and make those the start and goal automatically
    }

    public List<WaypointBlock> GetPath()
    {
        BreadthFirstSearch();
        CreatePath();
        ColorStartAndGoalBlocks();
        return path;
    }

    private void CreatePath()
    {
        path.Add(endWaypointBlock);
        WaypointBlock previous = endWaypointBlock.queuedFrom;
        while (previous != startWaypointBlock)
        {
            path.Add(previous);
            previous = previous.queuedFrom;
        }
        path.Add(startWaypointBlock);
        path.Reverse();
    }

    private void BreadthFirstSearch()
    {
        queue.Enqueue(startWaypointBlock);
        while(queue.Count > 0 && isRunning)
        {
            WaypointBlock searchCenter = queue.Dequeue();
            print("Searching from " + searchCenter); // TODO remove later
            HaltIfEndFoundFrom(searchCenter);
            ExploreNeighbors(searchCenter);
            searchCenter.isExplored = true;
        }
        print("Fishined Searching");
    }

    private void HaltIfEndFoundFrom(WaypointBlock searchCenter)
    {
        if (searchCenter == endWaypointBlock)
        {
            print("searching from end point, stopping search"); // TODO remove later
            isRunning = false;
        }
    }

    private void ExploreNeighbors(WaypointBlock from)
    {
        if (!isRunning) { return; }
        int i = 0;
        int x = from.GetGridPos().x;
        int y = from.GetGridPos().y;
        foreach (Vector2Int direction in pathDirections)
        {
            Vector2Int nextDirection = new Vector2Int(x + pathDirections[i].x, y + pathDirections[i].y);
            print("Exploring " + nextDirection + " from " + from);
            if (grid.ContainsKey(nextDirection))
            {
                QueueNewNeighbor(nextDirection, from);
            }
            else
            {
                print(nextDirection + " does not exist in the current grid");
            }
            i++;
        }
    }

    private void QueueNewNeighbor(Vector2Int nextDirection, WaypointBlock from)
    {
        if (grid[nextDirection].isExplored || queue.Contains(grid[nextDirection])) 
        {
            print(grid[nextDirection] + " has been explored already");
        }
        else
        {
            grid[nextDirection].SetTopColor(Color.blue); // todo move later?
            queue.Enqueue(grid[nextDirection]);
            grid[nextDirection].queuedFrom = from;
            print("Queueing " + grid[nextDirection] + " from " + from);
        }
    }

    private void ColorStartAndGoalBlocks()
    {
        startWaypointBlock.SetTopColor(Color.green);
        endWaypointBlock.SetTopColor(Color.red);
    }

    private void RemoveDuplicateBlocksFromChildList()
    {
        foreach (Transform duplicate in blocksToRemove)
        {
            childList.Remove(duplicate);
            Debug.LogWarning("removed " + duplicate + " from original childlist");
        }
    }

    private void BuildGridDictionary()
    {
        foreach (Transform gridBlock in childList)
        {
            WaypointBlock waypointBlock = gridBlock.GetComponent<WaypointBlock>();
            bool duplicateWaypoint = grid.ContainsKey(waypointBlock.GetGridPos());
            if (duplicateWaypoint)
            {
                CheckIfDuplicateIsImportant(waypointBlock);
                //Debug.LogWarning("Duplicate block " + waypointBlock);
                waypointBlock.gameObject.SetActive(false);
                blocksToRemove.Add(gridBlock);
                //Debug.LogWarning("added " + gridBlock + " to remove list");
            }
            else
            {
                grid.Add(waypointBlock.GetGridPos(), waypointBlock);
            }
            //print(grid);
            //print(grid.Count);
        }
    }

    private void CheckIfDuplicateIsImportant(WaypointBlock waypointBlock)
    {
        if (waypointBlock == startWaypointBlock)
        {
            startWaypointBlock = grid[waypointBlock.GetGridPos()];
        }
        if (waypointBlock == endWaypointBlock)
        {
            endWaypointBlock = grid[waypointBlock.GetGridPos()];
        }
    }

    private void BuildChildList()
    {
        foreach (Transform child in transform)
        {
            childList.Add(child);
        }
    }
}

and the EnemyMovement.cs script…

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

public class EnemyMovement : MonoBehaviour
{
    // SHIPPING DATA //


    // CHANGING DATA //
    [SerializeField] PathFinder myGrid;
    List<WaypointBlock> path = new List<WaypointBlock>();


    // * CODE * //

    // Start is called before the first frame update
    void Start()
    {
        List<WaypointBlock> path = myGrid.GetPath();
        StartCoroutine(FollowPath(path));
    }

    IEnumerator FollowPath(List<WaypointBlock> path)
    {
        print("Starting Patrol...");
        foreach (WaypointBlock cube in path)
        {
            transform.position = cube.transform.position;
            print("Visiting Block: " + cube.name);
            yield return new WaitForSeconds(1f);
        }
        print("Ending Patrol...");
    }
}

hope this helps anyone else who was trying to build similar functions but was having similar issues

Good job, and thanks for sharing your solution! :slight_smile:

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

Privacy & Terms