Move action not working with pathfinding

I have gone through my code several times and can’t find why my movewction no longer works. None of the tiles display walkable tiles. None of my units will take the move action any more. All other actions work and have the alternate color display.
No errors pop up, so I don’t know where to look.

The link to a zip of the project.

That didn’t come up as a valid project (I think it’s just the Scripts folder). I usually ask for a project upload (to our server) after we’ve done some basic troubleshooting, so let’s start there.

Let’s take a look at your MoveAction.cs and your Patfhinding.cs methods. Be sure to paste in the text and not a screenshot.

Sounds good. Hopefully it’s easy for you to spot. Here is the MoveAction:

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

public class MoveAction : BaseAction
{
    private List<Vector3> positionList;
    private int currentPositionIndex;
    public event EventHandler OnStartMoving;
    public event EventHandler OnStopMoving;

    [SerializeField]
    private int maxMoveDistance = 1;

    private void Update()
    {
        //This setups up a bool so if this code is not active then it will return
        if (!isActive)
        {
            return;
        }

        Vector3 targetPosition = positionList[currentPositionIndex];
        //Make the unit move in a certain direction and speed
        Vector3 moveDirection = (targetPosition - transform.position).normalized;

        //Setting the transform of the unit as it moves to face the space clicked
        float rotateSpeed = 15f;
        transform.forward = Vector3.Lerp(transform.forward, moveDirection, Time.deltaTime * rotateSpeed);

        //Learn the distance between the object and the unit moving and also making it stop and not shake
        float stoppingDistance = .1f;
        if (Vector3.Distance(transform.position, targetPosition) > stoppingDistance)
        {
            float moveSpeed = 4f;
            transform.position += moveDirection * moveSpeed * Time.deltaTime;
        }
        else
        {
            currentPositionIndex++;
            if (currentPositionIndex >= positionList.Count)
            {
                OnStopMoving?.Invoke(this, EventArgs.Empty);
                //Setting isActive false after the move is complete
                ActionComplete();
            }
        }

    }

    //This function is exposed so that we can move the unit without giving the rest of the code out
    public override void TakeAction(GridPosition gridPosition, Action onActionComplete)
    {
        GridPosition unitGridPostion = unit.GetGridPostion();
        List<GridPosition> pathGridPositionList = Pathfinding.Instance.FindPath(unitGridPostion, gridPosition, out int pathLength);
        currentPositionIndex = 0;
        positionList = new List<Vector3>();
        foreach (GridPosition pathGridPosition in pathGridPositionList)
        {
            positionList.Add(LevelGrid.Instance.GetWorldPosition(pathGridPosition));

        }
        //setting up the animator from the animation script for walking
        // Invoke the OnStartMoving event if it is not null
        if (OnStartMoving != null)
        {
            OnStartMoving?.Invoke(this, EventArgs.Empty);
        }
        //setting up the delegate to call from the base class
        ActionStart(onActionComplete);

    }


    //This list helps create the max move distance a unit can make from their current postion
    //You get the grid position to make the logic for understanding where the unit is and the max distance from that location
    public new bool IsValidActionGridPosition(GridPosition gridPosition)
    {
        List<GridPosition> validGridPositionList = GetValidActionGridPositionList();
        return validGridPositionList.Contains(gridPosition);
    }
    public override List<GridPosition> GetValidActionGridPositionList()
    {
        List<GridPosition> validGridPositionList = new List<GridPosition>();
        GridPosition unitGridPostion = unit.GetGridPostion();
        //This needs to cycle through the grid
        for (int x = -maxMoveDistance; x <= maxMoveDistance; x++)
        {
            for (int z = -maxMoveDistance; z <= maxMoveDistance; z++)
            {
                GridPosition offsetGridPostion = new GridPosition(x, z);
                GridPosition testGridPosition = unitGridPostion + offsetGridPostion;

                //This is the logic for moving the unit on the grid


                //If position is valid we want to keep it. If not we want to discard
                //that is what this boundary does. Calling from lvelgrid and gridsystem
                if (!LevelGrid.Instance.IsValidGridPosition(testGridPosition))
                {
                    //continue skips forward if the IF statement says that the iteration is not valid
                    //if it is valid then the code goes into the next step, currently debug
                    continue;
                }
                if (unitGridPostion == testGridPosition)
                {
                    //Same gridPosition where the unit is already at
                    continue;
                }


                if (LevelGrid.Instance.HasAnyUnitOnGridPosition(testGridPosition))
                {
                    //Making sure units don't stack on the same position
                    continue;
                }


                if (!Pathfinding.Instance.IsWalkAbleGridPosition(testGridPosition))
                {
                    //If tile is not walkable then pathfinding will ignore the tile
                    continue;
                }


                if (!Pathfinding.Instance.HasPath(unitGridPostion, testGridPosition))
                {
                    //If tile is not walkable then pathfinding will ignore the tile
                    continue;
                }

                //This prevents the path from reading tiles through walls as walkable for the player
                int pathfindingDistanceMultiplier = 10;
                if (Pathfinding.Instance.GetPathLength(unitGridPostion, testGridPosition) > maxMoveDistance * pathfindingDistanceMultiplier)
                {
                    //Path length is too long
                    continue;
                }
                validGridPositionList.Add(testGridPosition);
            }
        }

        return validGridPositionList;
    }

    //Overrides the name for the action button
    public override string GetActionName()
    {
        return "Move";
    }

    public override int GetActionPointsCost()
    {
        return 1;
    }


    //This is setup for the enemy action override
    public override EnemyAIAction GetEnemyAIAction(GridPosition gridPosition)
    {
        //This helps determine to shoot before moving and the importance of it
        int targetCountAtGridPosition = unit.GetAction<ShootAction>().GetTargetCountAtPosition(gridPosition);
        return new EnemyAIAction
        {
            gridPosition = gridPosition,
            actionValue = targetCountAtGridPosition * 10,
        };
    }
}

and here is the Pathfinding

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

public class Pathfinding : MonoBehaviour
{
    public static Pathfinding Instance { get; private set; }

    //Defining movement costs
    private const int MOVE_STRAIGHT_COST = 10;
    private const int MOVE_DIAGNOL_COST = 14;

    [SerializeField]
    private LayerMask obstaclesLayerMask;
    [SerializeField]
    private Transform gridDebugObjectPrefab;
    private int width;
    private int height;
    private float cellSize;
    private GridSystem<Pathnode> gridSystem;

    private void Awake()
    {
        if(Instance != null)
        {
            Debug.LogError("There is more than one Pathfinding!" + transform + " " + Instance);
            Destroy(gameObject);
            return;
        }

        Instance = this;
    }

    public void Setup(int width, int height, float cellSize)
    {
        this.width = width;
        this.height = height;
        this.cellSize = cellSize;

        //making the gridsystem
        gridSystem = new GridSystem<Pathnode>(width, height, cellSize,
            (GridSystem<Pathnode> g, GridPosition gridPosition) => new Pathnode(gridPosition));

        //making the gridobject display all the text and color of the iswalkable. Commenting this out hides all that
        // gridSystem.CreateDebugObjects(gridDebugObjectPrefab);

        //cycle through the width and height
        //This is how you call the raycast to hit the obstacles if there is one
        //then you call get node to say that the obstacle is unwalkable
        for(int x = 0; x < width; x ++)
        {
            for (int z = 0; z < height; z++)
            {
                GridPosition gridPosition = new GridPosition(x, z);
                //Origin position
                Vector3 worldPosition = LevelGrid.Instance.GetWorldPosition(gridPosition);
                //The offset gets around the objects collider and prevents an error
                float raycastOffsetDistance = 5f;
                if (Physics.Raycast(
                worldPosition +
                Vector3.down *
                raycastOffsetDistance,
                Vector3.up,
                raycastOffsetDistance * 2,
                obstaclesLayerMask))
                { 
                    GetNode(x, z).SetIsWalkable(false); 
                }

            }
        }

    }

    public List<GridPosition> FindPath(GridPosition startGridPosition, GridPosition endGridPosition, out int pathLength)
    {
        //Setup the open and close list first
        //openlist contains all the nodes that are for searching
        List<Pathnode> openlist = new List<Pathnode>();
        //the closed list are all the nodes that we already searched
        List<Pathnode> closedList = new List<Pathnode>();

        Pathnode startNode = gridSystem.GetGridObject(startGridPosition);
        Pathnode endNode = gridSystem.GetGridObject(endGridPosition);
        openlist.Add(startNode);

        for (int x = 0; x < gridSystem.GetWidth(); x++)
        {
            for(int z = 0; z < gridSystem.GetHeight(); z++)
            {
                GridPosition gridPosition = new GridPosition(x, z);
                Pathnode pathnode = gridSystem.GetGridObject(gridPosition);

                pathnode.SetGCost(int.MaxValue);
                pathnode.SetHCost(0);
                pathnode.CalculateFCost();
                //The reset node makes it so the previously calculated path doesn't show up when recalculating a new node
                pathnode.ResetCameFromPathnode();
            }
        }
        //This is the setup for the path
        //The G cost for the begining node should always be 0 since its where the path begins
        startNode.SetGCost(0);
        startNode.SetHCost(CalculateDistance(startGridPosition, endGridPosition));
        startNode.CalculateFCost();

        while (openlist.Count > 0)
        {
            Pathnode currentNode = GetLowestFCostPathnode(openlist);

            if(currentNode == endNode)
            {
                pathLength = endNode.GetFCost();
                //we have reached the final node in the path
                return CalculatePath(endNode);
            }

            openlist.Remove(currentNode);
            closedList.Add(currentNode);

            foreach(Pathnode neighborNode in GetNeighborList(currentNode))
            {
                if(closedList.Contains(neighborNode))
                {
                    continue;
                }
                //create a is this walkable check
                if(neighborNode.IsWalkable())
                {
                    closedList.Add(neighborNode);
                    continue;
                }


                int tentativeGCost = 
                    currentNode.GetGCost() + CalculateDistance(currentNode.GetGridPosition(), neighborNode.GetGridPosition());
                
                if(tentativeGCost < neighborNode.GetGCost())
                {
                    neighborNode.SetCameFromPathnode(currentNode);
                    neighborNode.SetGCost(tentativeGCost);
                    neighborNode.SetHCost(CalculateDistance(neighborNode.GetGridPosition(), endGridPosition));
                    neighborNode.CalculateFCost();

                    if(!openlist.Contains(neighborNode))
                    {
                        openlist.Add(neighborNode);
                    }
                }

            }

        }
        //No path found
        pathLength = 0;
        return null;

    }
    
    public int CalculateDistance(GridPosition gridPositionA, GridPosition gridPositionB)
    {
        GridPosition gridPositionDistance = gridPositionA - gridPositionB;
       // use this if you don't want diagnol movement => int distance = Mathf.Abs(gridPositionDistance.x) + Mathf.Abs(gridPositionDistance.z);
        //This sets up the diagnol movement cost delete all below but the last comment if you don't want diagnol
        int xDistance = Mathf.Abs(gridPositionDistance.x);
        int zDistance = Mathf.Abs(gridPositionDistance.z);
        int remaining = Mathf.Abs(xDistance - zDistance);
        return MOVE_DIAGNOL_COST * Mathf.Min(xDistance, zDistance) + MOVE_STRAIGHT_COST * remaining; ;

        // delete the diagnol bits if you don't want diagnol and use this => return distance * MOVE_STRAIGHT_COST;
    }

    private Pathnode GetLowestFCostPathnode(List<Pathnode> pathnodeList)
    {
        Pathnode lowestFCostPathNode = pathnodeList[0];
        for(int i = 0; i < pathnodeList.Count; i++)
        {
            if(pathnodeList[i].GetFCost() < lowestFCostPathNode.GetFCost())
            {
                lowestFCostPathNode = pathnodeList[i];

            }
        }
        return lowestFCostPathNode;
    }


    private Pathnode GetNode(int x , int z)
    {
        return gridSystem.GetGridObject(new GridPosition(x, z));
    }
    //looking at neighbor nodes 
    private List<Pathnode> GetNeighborList(Pathnode currentNode)
    {
        List<Pathnode> neighborList = new List<Pathnode>();

        GridPosition gridPosition = currentNode.GetGridPosition();
        if (gridPosition.x - 1 >= 0)
        {
            //This is to get the neighbor on the left
            neighborList.Add(GetNode(gridPosition.x - 1, gridPosition.z + 0));
            if (gridPosition.z - 1 >= 0)
            {
                //This is to get the neighbor on the left down DELETE THIS IF YOU DON"T WANT DIAGNOL MOVEMENT
                neighborList.Add(GetNode(gridPosition.x - 1, gridPosition.z - 1));
            }
            if(gridPosition.z + 1 <gridSystem.GetHeight())
            { 
                //This is to get the neighbor on the left up DELETE THIS IF YOU DON"T WANT DIAGNOL MOVEMENT
                neighborList.Add(GetNode(gridPosition.x - 1, gridPosition.z + 1));
            }
        }
        if (gridPosition.x + 1 < gridSystem.GetWidth())
        {
            //Right
            neighborList.Add(GetNode(gridPosition.x + 1, gridPosition.z + 0));
            if (gridPosition.z - 1 >= 0)
            {
                //Right Down DELTET THIS IF YOU DON"T WANT DIAGNOL MOVEMENT
                neighborList.Add(GetNode(gridPosition.x + 1, gridPosition.z - 1));
            }
            if (gridPosition.z + 1 < gridSystem.GetHeight())
            {
                //Right Up DELETE THIS IF YOU DON"T WANT DIAGNOL MOVEMENT
                neighborList.Add(GetNode(gridPosition.x + 1, gridPosition.z + 1));
            }
        }
        if (gridPosition.z - 1 >= 0)
        {
            //Down
            neighborList.Add(GetNode(gridPosition.x + 0, gridPosition.z - 1));
        }
        if (gridPosition.z  + 1 < gridSystem.GetHeight())
        {
            //UP
            neighborList.Add(GetNode(gridPosition.x + 0, gridPosition.z + 1));
        }
        return neighborList;
    }

    private List<GridPosition>CalculatePath(Pathnode endNode)
    {
        List<Pathnode> pathNodeList = new List<Pathnode>();
        pathNodeList.Add(endNode);
        Pathnode currentNode = endNode;
        while (currentNode.GetFromPathNode() != null)
        {
            pathNodeList.Add(currentNode.GetFromPathNode());
            currentNode = currentNode.GetFromPathNode();
        }

        pathNodeList.Reverse();

        List<GridPosition> gridPositionList = new List<GridPosition>();
        foreach (Pathnode pathnode in pathNodeList)
        {
            gridPositionList.Add(pathnode.GetGridPosition());
                           
        }
        return gridPositionList;
    }

    public bool IsWalkAbleGridPosition(GridPosition gridPosition)
    {
       return gridSystem.GetGridObject(gridPosition).IsWalkable();
    }
    public bool HasPath(GridPosition startGridPosition, GridPosition endGridPosition)
    {
        //This takes tiles off if they are unreachable and will not display them as walkable
        return FindPath(startGridPosition, endGridPosition, out int pathlength) != null;
    }

    public int GetPathLength(GridPosition startGridPosition, GridPosition endGridPosition)
    {
        FindPath(startGridPosition, endGridPosition, out int pathLength);
        return pathLength;
    }
    
}

There are a couple of possibilities that come to mind:
First, make sure that you’ve set the proper layers on the obstacleLayerMask. Remember that it defaults to basically “everything” which would wind up marking all tiles as unwalkable.
Second, make sure in your PathNode that isWalkable is default to true

[SerializeField] bool isWalkable = true;

I did as you said and now I do have an error popping up, but still no movement.

NullReferenceException: Object reference not set to an instance of an object
GridSystemVisual.UpdateGridVisual () (at Assets/Scripts/Grid/GridSystemVisual.cs:172)
GridSystemVisual.Start () (at Assets/Scripts/Grid/GridSystemVisual.cs:69)

Here is my Pathnode and GridVisual

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

public class Pathnode 
{
    private GridPosition gridPosition;
    private int gCost;
    private int hCost;
    private int fCost;
    private Pathnode cameFromPathnode;
    private bool isWalkable = true;


    public Pathnode(GridPosition gridPosition)
    {
        this.gridPosition = gridPosition;
    }

    public override string ToString()
    {
        return gridPosition.ToString();
    }

    public int GetGCost()
    {
        return gCost;
    } 
    public int GetHCost()
    {
        return hCost;
    } public int GetFCost()
    {
        return fCost;
    }

    public void SetGCost(int gCost)
    {
        this.gCost = gCost;
    }
    public void SetHCost(int hCost)
    {
        this.hCost = hCost;
    }

    public void CalculateFCost()
    {
        fCost = gCost + hCost;
    }

    public void ResetCameFromPathnode()
    {
        cameFromPathnode = null;
    }

    public GridPosition GetGridPosition()
    {
        return gridPosition;
    }


    public void SetCameFromPathnode(Pathnode pathnode)
    {
        cameFromPathnode = pathnode;
    }
    public Pathnode GetFromPathNode()
    {
        return cameFromPathnode;
    }

    public bool IsWalkable()
    {
        return isWalkable;
    }

    public void SetIsWalkable(bool isWalkable)
    {
        this.isWalkable = isWalkable;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class GridSystemVisual : MonoBehaviour
{
    public static GridSystemVisual Instance { get; private set; }
    //Creating different colors for the grid display for each individual action
    //The struct allows flexibility for color change without having to have multiple serializedfields for each one
   [Serializable] //This is used to make the struct show in the editor
    public struct GridVisualTypeMaterial
    {
        public GridVisualType gridVisualType;
        public Material material;
    }
    public enum GridVisualType
    {
        White,
        Blue,
        Red,
        RedSoft,
        Yellow
    }

    [SerializeField]
    private Transform gridSystemVisualSinglePrefab;
    [SerializeField]
    private List<GridVisualTypeMaterial> gridVisualTypeMaterialList;
    private GridSystemVisualSingle[,] gridSystemVisualSingleArray;

    private void Awake()
    {
        //Log this error as a way to avoid making multiple of this gameobject. This is desgined to prevent the game from crashing
        if (Instance != null)
        {
            Debug.LogError("There is more than one gridsystemviaual instance!" + transform + "..." + Instance);
            Destroy(gameObject);
            return;
        }
        Instance = this;
    }
    private void Start()
    {
        //Setup our array for the grid visual
        gridSystemVisualSingleArray = new GridSystemVisualSingle[
            LevelGrid.Instance.GetWidth(),
            LevelGrid.Instance.GetHeight()
            ];



        //This is calling the passthrough from levelgrid and gridsystem to access the width and height to set all the prefabs on gridpositions
        for (int x = 0; x < LevelGrid.Instance.GetWidth(); x++)
        {
            for (int z = 0; z < LevelGrid.Instance.GetHeight(); z++)
            {
                GridPosition gridPosition = new GridPosition(x, z);
                Transform gridSystemVisualSingleTransform = 
                    Instantiate(gridSystemVisualSinglePrefab, LevelGrid.Instance.GetWorldPosition(gridPosition), Quaternion.identity);

                gridSystemVisualSingleArray[x, z] = gridSystemVisualSingleTransform.GetComponent<GridSystemVisualSingle>();
            }
        }

        //Set up the event to update the grid when an action changes not on a constant update
        UnitActionSyste.Instance.OnSelectedActionChanged += UniActionSyste_OnSelectedActionChanged;
        LevelGrid.Instance.OnAnyUnitMovedGridPosition += LevelGrid_OnAnyUnitMovedGridPosition;
        UpdateGridVisual();
    }

    

    public void HideAllGridPosition()
    {
        for (int x = 0; x < LevelGrid.Instance.GetWidth(); x++)
        {
            for (int z = 0; z < LevelGrid.Instance.GetHeight(); z++)
            {

                gridSystemVisualSingleArray[x, z].Hide();
            }
        }
    }

    //This will show different colors based on the range of the action
    private void ShowGridPositionRangeSquare(GridPosition gridPosition, int range, GridVisualType gridVisualType)
    {
        List<GridPosition> gridPositionList = new List<GridPosition>();
        for (int x = -range; x <= range; x++)
        {
            for (int z = -range; z <= range; z++)
            {
                //Test for gridposition to create this ciruclar gridposition
                GridPosition testGridPosition = gridPosition + new GridPosition(x, z);

                //Validate the gridposition first and then do the rest
                if(!LevelGrid.Instance.IsValidGridPosition(testGridPosition))
                {
                    continue;
                }    
                gridPositionList.Add(testGridPosition);
            }
        }
        ShowGridPositionList(gridPositionList, gridVisualType);
    }
    private void ShowGridPositionRange(GridPosition gridPosition, int range, GridVisualType gridVisualType)
    {
        List<GridPosition> gridPositionList = new List<GridPosition>();
        for (int x = -range; x <= range; x++)
        {
            for (int z = -range; z <= range; z++)
            {
                //Test for gridposition to create this ciruclar gridposition
                GridPosition testGridPosition = gridPosition + new GridPosition(x, z);

                //Validate the gridposition first and then do the rest
                if(!LevelGrid.Instance.IsValidGridPosition(testGridPosition))
                {
                    continue;
                }    

                int testDistance = (int)(Mathf.Abs(x) + MathF.Abs(z));
                if (testDistance > range)
                {
                    continue;
                }
                gridPositionList.Add(testGridPosition);
            }
        }
        ShowGridPositionList(gridPositionList, gridVisualType);
    }

    public void ShowGridPositionList(List<GridPosition> gridPositionList, GridVisualType gridVisualType)
    {
        foreach (GridPosition gridPosition in gridPositionList)
        {
            gridSystemVisualSingleArray[gridPosition.x, gridPosition.z].Show(GetGridVisualTypeMaterial(gridVisualType));
        }
    }
    private void UpdateGridVisual()
    {
        HideAllGridPosition();

        //Access the unit for this function to work
        Unit selectedUnit = UnitActionSyste.Instance.GetSelectedUnit();
        BaseAction selectedAction = UnitActionSyste.Instance.GetSelectedAction();

        //cycle through colors based on the action with this switch
        GridVisualType gridVisualType;
        switch(selectedAction)
        {
            default:
            case MoveAction moveAction:
                gridVisualType = GridVisualType.White;
                break; 
            case SpinAction spinAction:
                gridVisualType = GridVisualType.Blue;
                break; 
            case GrenadeAction grenadeAction:
                gridVisualType = GridVisualType.Yellow;
                break; 
            case ShootAction shootAction:
                gridVisualType = GridVisualType.Red;
                ShowGridPositionRange(selectedUnit.GetGridPostion(), shootAction.GetTheMaxShootDistance(), GridVisualType.RedSoft);
                break; 
            case SwordAction swordAction:
                gridVisualType = GridVisualType.Red;
                ShowGridPositionRangeSquare(selectedUnit.GetGridPostion(), swordAction.GetMaxSwordDistance(), GridVisualType.RedSoft);
                break;
        }
        ShowGridPositionList(
            selectedAction.GetValidActionGridPositionList(), gridVisualType);

    }

    private void UniActionSyste_OnSelectedActionChanged(object sender, EventArgs e)
    {
        UpdateGridVisual();
    }
    private void LevelGrid_OnAnyUnitMovedGridPosition(object sender, EventArgs e)
    {
        UpdateGridVisual();
    }

    private Material GetGridVisualTypeMaterial(GridVisualType gridVisualType)
    {
        foreach(GridVisualTypeMaterial gridVisualTypeMaterial in  gridVisualTypeMaterialList)
        {
            if(gridVisualTypeMaterial.gridVisualType == gridVisualType)
            {
                return gridVisualTypeMaterial.material;
            }
        }
        Debug.LogError("Could not find GridVisualTypeMaterial for GridVisualType " + gridVisualType);
        return null;
    }

}

The things that look like it could be null at the start with the UpdateGridVisual is the selectedAction…
Let’s see if this is the case with some Debugs:
After BaseAction selectedAction = UnitActionSyste.Instance.GetSelectedAction();

if(selectedUnit==null)
{
    Debug.Log($"UpdateGridVisual() -- selected is null!");
    return; //no point in continuing
}
if(selectedAction == null)
{
    Debug.Log($"UpdateGridVisual() -- selectedAction is null!");
    return; //again no point in continuing
}

If either of these are null, then it’s possible you have a race condition going on.

I added these checks and it does look like the selectedAction is what is coming back as null. Would this be something that just needs to be fixed in the script execution order?

Yes, it looks like you have a race condition going on. The UnitActionSystem has selected a Unit, but it hasn’t selected an Action yet by the time Start() fires. That’s very likely script execution order.

I played around with the order a bit and couldn’t get it to change.
Here is a snip of the order. Any suggestions on what to swap or add?

This is where I’m unsure, as I’m not able to duplicate this issue on my copy of the project.

I think there may be a better solution using the Debug sequence I posted earlier… what we’re going to do is defer the UpdateGridVisual if the selectedAction is null

if(selectedUnit==null || selectedAction == null) 
{
    Invoke(nameOf(UpdateGridVisual), .1f);
    Debug.Log($"{Deferring UpdateGridVisual");
    return;
}

This is technically a bandaid, but it should prevent the null reference and is quick enough it won’t be noticeable to the player. What this does is determine that “Oh, the action hasn’t been selected yet, so we’ll just wait a moment and try again”.

I plugged that in but it does show an error. Is there a System or dictionary I should add to use this properly? I have never used the “name of” function before. Or did I just implement it wrong?

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

public class GridSystemVisual : MonoBehaviour
{
    public static GridSystemVisual Instance { get; private set; }
    //Creating different colors for the grid display for each individual action
    //The struct allows flexibility for color change without having to have multiple serializedfields for each one
   [Serializable] //This is used to make the struct show in the editor
    public struct GridVisualTypeMaterial
    {
        public GridVisualType gridVisualType;
        public Material material;
    }
    public enum GridVisualType
    {
        White,
        Blue,
        Red,
        RedSoft,
        Yellow
    }

    [SerializeField]
    private Transform gridSystemVisualSinglePrefab;
    [SerializeField]
    private List<GridVisualTypeMaterial> gridVisualTypeMaterialList;
    private GridSystemVisualSingle[,] gridSystemVisualSingleArray;

    private void Awake()
    {
        //Log this error as a way to avoid making multiple of this gameobject. This is desgined to prevent the game from crashing
        if (Instance != null)
        {
            Debug.LogError("There is more than one gridsystemviaual instance!" + transform + "..." + Instance);
            Destroy(gameObject);
            return;
        }
        Instance = this;
    }
    private void Start()
    {
        //Setup our array for the grid visual
        gridSystemVisualSingleArray = new GridSystemVisualSingle[
            LevelGrid.Instance.GetWidth(),
            LevelGrid.Instance.GetHeight()
            ];



        //This is calling the passthrough from levelgrid and gridsystem to access the width and height to set all the prefabs on gridpositions
        for (int x = 0; x < LevelGrid.Instance.GetWidth(); x++)
        {
            for (int z = 0; z < LevelGrid.Instance.GetHeight(); z++)
            {
                GridPosition gridPosition = new GridPosition(x, z);
                Transform gridSystemVisualSingleTransform = 
                    Instantiate(gridSystemVisualSinglePrefab, LevelGrid.Instance.GetWorldPosition(gridPosition), Quaternion.identity);

                gridSystemVisualSingleArray[x, z] = gridSystemVisualSingleTransform.GetComponent<GridSystemVisualSingle>();
            }
        }

        //Set up the event to update the grid when an action changes not on a constant update
        UnitActionSyste.Instance.OnSelectedActionChanged += UniActionSyste_OnSelectedActionChanged;
        LevelGrid.Instance.OnAnyUnitMovedGridPosition += LevelGrid_OnAnyUnitMovedGridPosition;
        UpdateGridVisual();
    }

    

    public void HideAllGridPosition()
    {
        for (int x = 0; x < LevelGrid.Instance.GetWidth(); x++)
        {
            for (int z = 0; z < LevelGrid.Instance.GetHeight(); z++)
            {

                gridSystemVisualSingleArray[x, z].Hide();
            }
        }
    }

    //This will show different colors based on the range of the action
    private void ShowGridPositionRangeSquare(GridPosition gridPosition, int range, GridVisualType gridVisualType)
    {
        List<GridPosition> gridPositionList = new List<GridPosition>();
        for (int x = -range; x <= range; x++)
        {
            for (int z = -range; z <= range; z++)
            {
                //Test for gridposition to create this ciruclar gridposition
                GridPosition testGridPosition = gridPosition + new GridPosition(x, z);

                //Validate the gridposition first and then do the rest
                if(!LevelGrid.Instance.IsValidGridPosition(testGridPosition))
                {
                    continue;
                }    
                gridPositionList.Add(testGridPosition);
            }
        }
        ShowGridPositionList(gridPositionList, gridVisualType);
    }
    private void ShowGridPositionRange(GridPosition gridPosition, int range, GridVisualType gridVisualType)
    {
        List<GridPosition> gridPositionList = new List<GridPosition>();
        for (int x = -range; x <= range; x++)
        {
            for (int z = -range; z <= range; z++)
            {
                //Test for gridposition to create this ciruclar gridposition
                GridPosition testGridPosition = gridPosition + new GridPosition(x, z);

                //Validate the gridposition first and then do the rest
                if(!LevelGrid.Instance.IsValidGridPosition(testGridPosition))
                {
                    continue;
                }    

                int testDistance = (int)(Mathf.Abs(x) + MathF.Abs(z));
                if (testDistance > range)
                {
                    continue;
                }
                gridPositionList.Add(testGridPosition);
            }
        }
        ShowGridPositionList(gridPositionList, gridVisualType);
    }

    public void ShowGridPositionList(List<GridPosition> gridPositionList, GridVisualType gridVisualType)
    {
        foreach (GridPosition gridPosition in gridPositionList)
        {
            gridSystemVisualSingleArray[gridPosition.x, gridPosition.z].Show(GetGridVisualTypeMaterial(gridVisualType));
        }
    }
    private void UpdateGridVisual()
    {
        HideAllGridPosition();

        //Access the unit for this function to work
        Unit selectedUnit = UnitActionSyste.Instance.GetSelectedUnit();
        BaseAction selectedAction = UnitActionSyste.Instance.GetSelectedAction();
        if (selectedUnit == null)
        {
            Debug.Log($"UpdateGridVisual() -- selected is null!");
            return; //no point in continuing
        }
        if (selectedAction == null)
        {
            Debug.Log($"UpdateGridVisual() -- selectedAction is null!");
            return; //again no point in continuing
        }
        if (selectedUnit == null || selectedAction == null)
        {
            Invoke(nameOf(UpdateGridVisual), .1f);
            Debug.Log($"{Deferring UpdateGridVisual");
    return;
        }

        //cycle through colors based on the action with this switch
        GridVisualType gridVisualType;
        switch(selectedAction)
        {
            default:
            case MoveAction moveAction:
                gridVisualType = GridVisualType.White;
                break; 
            case SpinAction spinAction:
                gridVisualType = GridVisualType.Blue;
                break; 
            case GrenadeAction grenadeAction:
                gridVisualType = GridVisualType.Yellow;
                break; 
            case ShootAction shootAction:
                gridVisualType = GridVisualType.Red;
                ShowGridPositionRange(selectedUnit.GetGridPostion(), shootAction.GetTheMaxShootDistance(), GridVisualType.RedSoft);
                break; 
            case SwordAction swordAction:
                gridVisualType = GridVisualType.Red;
                ShowGridPositionRangeSquare(selectedUnit.GetGridPostion(), swordAction.GetMaxSwordDistance(), GridVisualType.RedSoft);
                break;
        }
        ShowGridPositionList(
            selectedAction.GetValidActionGridPositionList(), gridVisualType);

    }

    private void UniActionSyste_OnSelectedActionChanged(object sender, EventArgs e)
    {
        UpdateGridVisual();
    }
    private void LevelGrid_OnAnyUnitMovedGridPosition(object sender, EventArgs e)
    {
        UpdateGridVisual();
    }

    private Material GetGridVisualTypeMaterial(GridVisualType gridVisualType)
    {
        foreach(GridVisualTypeMaterial gridVisualTypeMaterial in  gridVisualTypeMaterialList)
        {
            if(gridVisualTypeMaterial.gridVisualType == gridVisualType)
            {
                return gridVisualTypeMaterial.material;
            }
        }
        Debug.LogError("Could not find GridVisualTypeMaterial for GridVisualType " + gridVisualType);
        return null;
    }

}

Actually, it was my fault… I get so used to typing things in camelCase that I forgot that nameof contains no capitalization.

Invoke(nameof(UpdateGridVisual), .1f); //Edited due to failure to close bracket

nameof just returns the name of a member as a string. Like when we Invoke a method

private void Start()
{
    Invoke("DoSomething", 1f);
}

private void DoSomething()
{
    Debug.Log("Done something");
}

it is easy to make a typo in the string above. We can use nameof to prevent that

private void Start()
{
    Invoke(nameof(DoSomething), 1f);
}

private void DoSomething()
{
    Debug.Log("Done something");
}

This is now a ‘design time’ check and will let us know immediately if we made a mistake. It also comes with IntelliSense.

I know this doesn’t solve your problem, but now you know about nameof

And you didn’t close the bracket. :wink:

1 Like

I was testing your code review skills. You passed 100%

1 Like

Thank you for explaining that. It is a cool tool, thank you both for showing and walking me through how to use it.

Doing this looks like it narrowed it down to my ShowGridPosition() on the GridSystem script.

    ShowGridPositionList(
        selectedAction.GetValidActionGridPositionList(), gridVisualType);

Pretty much what I suspected before the debugs I suggested. The problem is that somehow the selectedAction hasn’t been set yet in UnitActionSystem by the time this script runs at the same time. Did you try my Invoke patch in the beginning of UpdateGridVisual?

if(selectedUnit==null || selectedAction == null) 
{
    Invoke(nameOf(UpdateGridVisual), .1f);
    Debug.Log($"{Deferring UpdateGridVisual");
    return;
}

I tried that and it does make the error go away, however, I still am unable to make the moveAction work. Which is strange, because all other actions work with the switch.

Privacy & Terms