Implementing Fog of War

Hello, I have been working on implementing a fog of war feature, similar to CodeMonkey’s version shown in his 25-hour XCOM game but I’ve been running into an issue. I’ve been receiving a NullReferenceException on this FogOfWarVisual script when I call the

public void HideAllGridPositions()
    {
         for (int x = 0; x < LevelGrid.Instance.GetWidth(); x++)
        {
            for (int z = 0; z < LevelGrid.Instance.GetHeight(); z++)
            {
                Debug.Log("Hiding GridPositions!");
                fogOfWarVisualSingleArray[x, z].Show();
            }
        }
    }

I’ve been replicating the GridSystemVisual but I always am getting this issue.

Here are my scripts if anyone was wanting to try and replicate. I created the an empty game object and added a quad as the child which serves as my fog of war visual.

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

public class FogOfWarVisual : MonoBehaviour 
{

    public static FogOfWarVisual Instance { get; private set; }


    [SerializeField] private Transform FogOfWarVisualSinglePrefab;

    [SerializeField] private FogOfWarVisualSingle[,] fogOfWarVisualSingleArray;

    private void Awake() 
    {
        if (Instance != null)
        {
            Debug.LogError("More than one FogOfWarVisual" + transform + " - " + Instance);
            Destroy(gameObject);
            return;
        }
        Instance = this;
    }

    private void Start()
    {
        fogOfWarVisualSingleArray = new FogOfWarVisualSingle[
            LevelGrid.Instance.GetWidth(),
            LevelGrid.Instance.GetHeight()];

        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 fogOfWarVisualSingleTransform =
                Instantiate(FogOfWarVisualSinglePrefab, LevelGrid.Instance.GetWorldPosition(gridPosition), Quaternion.identity);
                fogOfWarVisualSingleTransform.parent = transform;
                //This literally matches the Grid System Visual WHY WONT IT WORK
                fogOfWarVisualSingleArray[x,z] = fogOfWarVisualSingleTransform.GetComponent<FogOfWarVisualSingle>();
            }
        }
    }

    //Hide all Grid positions, which means you show the Fog
    public void HideAllGridPositions()
    {
         for (int x = 0; x < LevelGrid.Instance.GetWidth(); x++)
        {
            for (int z = 0; z < LevelGrid.Instance.GetHeight(); z++)
            {
                Debug.Log("Hiding GridPositions!");
                fogOfWarVisualSingleArray[x, z].Show();
            }
        }
    }

    //Show the GridPositions, which means hiding certain Fog
    public void ShowGridPositionList(List<GridPosition> fogPositionList)
    {
        foreach(GridPosition gridPosition in fogPositionList)
        {
            fogOfWarVisualSingleArray[gridPosition.x,gridPosition.z].Hide();
        }
    }

}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using CodeMonkey.Utils;

public class FogOfWar : MonoBehaviour {

    [SerializeField] private LayerMask obstaclesLayerMask;

    private void Start() 
    {
        UnitManager.Instance.OnAnyUnitMovedGridPosition += UnitManager_OnAnyUnitMovedGridPosition;
        UpdateAllFogOfWar();
    }

    private void UnitManager_OnAnyUnitMovedGridPosition(object sender, System.EventArgs e) 
    {
        UpdateAllFogOfWar();
    }


    private void UpdateAllFogOfWar()
    {
        FogOfWarVisual.Instance.HideAllGridPositions();

        List<GridPosition> revealedGridPositionList = new List<GridPosition>();

        foreach(Unit unit in UnitManager.Instance.GetFriendlyUnitList())
        {
            GridPosition unitGridPosition = unit.GetGridPosition();

            var unitWorldPosition = LevelGrid.Instance.GetWorldPosition(unitGridPosition);

            revealedGridPositionList.Add(unitGridPosition);

            TestFogOfWarOnPosition(unitWorldPosition, ref revealedGridPositionList);

            List<GridPosition> neighbourGridOffsetList = new List<GridPosition>
            {
                new GridPosition(+1, +0),
                new GridPosition(-1, +0),
                new GridPosition(+0, +1),
                new GridPosition(+0, -1),

                new GridPosition(+1, +1),
                new GridPosition(-1, +1),
                new GridPosition(+1, -1),
                new GridPosition(-1, -1),
            };

            foreach(GridPosition neighbourGridOffset in neighbourGridOffsetList)
            {
                GridPosition neighbourGridPosition = unitGridPosition + neighbourGridOffset;

                if(LevelGrid.Instance.IsValidGridPosition(neighbourGridPosition))
                {
                    //Valid Position
                    Vector3 neighbourWorldPosition = LevelGrid.Instance.GetWorldPosition(neighbourGridPosition);
                    TestFogOfWarOnPosition(neighbourWorldPosition, ref revealedGridPositionList);
                }
            }
        }

        FogOfWarVisual.Instance.ShowGridPositionList(revealedGridPositionList);

/*
        //Show/Hide all enemies in the revealed grid positions
        foreach (Unit enemyUnit in UnitManager.Instance.GetEnemyUnitList()) 
        {
            if (revealedGridPositionList.Contains(enemyUnit.GetGridPosition())) {
                // Enemy visible
                enemyUnit.ShowVisual();
            } else {
                // Enemy invisible
                enemyUnit.HideVisual();
            }
        }
*/
    }

    private void TestFogOfWarOnPosition(Vector3 unitWorldPosition, ref List<GridPosition> revealedGridPositionList)
    {
        Vector3 baseDir = new Vector3(1,0,0);
        float angleIncrease = 10;
        for(float angle = 0; angle<360; angle += angleIncrease)
        {
            Vector3 dir = UtilsClass.ApplyRotationToVectorXZ(baseDir, angle);

            float viewDistanceMax = 14f;
            float viewDistanceIncrease = .4f;
            for(float viewDistance = 0f; viewDistance<viewDistanceMax; viewDistance += viewDistanceIncrease)
            {
                Vector3 targetPosition = unitWorldPosition + dir * viewDistance;
                GridPosition targetGridPosition = LevelGrid.Instance.GetGridPosition(targetPosition);

                foreach(Unit unit in UnitManager.Instance.GetFriendlyUnitList())
                {
                    GridPosition unitGridPosition = unit.GetGridPosition();

                    if(LevelGrid.Instance.IsValidGridPosition(targetGridPosition))
                    {
                        //Valid Grid Position
                        if(ObstacleInTheWay(unitGridPosition, targetGridPosition)) break;
                    }

                    if(!revealedGridPositionList.Contains(targetGridPosition))
                    {
                        revealedGridPositionList.Add(targetGridPosition);
                    }
                }

            }
        }
    }

        private bool ObstacleInTheWay(GridPosition unitGridPosition, GridPosition targetGridPosition)
    {
        // Get the unit world position
        var unitWorldPosition = LevelGrid.Instance.GetWorldPosition(unitGridPosition);
        // Get the target world position
        var targetWorldPosition = LevelGrid.Instance.GetWorldPosition(targetGridPosition);
        // Get the direction to the target
        var directionToTarget = (targetWorldPosition - unitWorldPosition).normalized;
        // Get the distance to the target
        var distanceToTarget = Vector3.Distance(unitWorldPosition, targetWorldPosition);
        // Create the ray - with height offset
        var offset = Vector3.up * 1f; // <- magic number. sorry
        var ray = new Ray(unitWorldPosition + offset, directionToTarget);
        // Check if there is an obstacle in the way and return
        return Physics.Raycast(ray, distanceToTarget, obstaclesLayerMask);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FogOfWarVisualSingle : MonoBehaviour
{
    [SerializeField] private MeshRenderer meshRenderer;

    public void Show()
    {
        meshRenderer.enabled = true;
    }

    public void Hide()
    {
        meshRenderer.enabled = false;
    }
}

I will continue to mess with it but if anyone has any suggestions I’m always willing to listen!

I tested this code and got a very different error from what you get.

Your error may just be because you didn’t put the FogOfWarVisualSingle component on the prefab, or you put it on a child object instead of the root.

I get an error because the TestFogOfWarOnPosition is including invalid grid positions. To fix this, I just moved some code

if(LevelGrid.Instance.IsValidGridPosition(targetGridPosition))
{
    //Valid Grid Position
    if(ObstacleInTheWay(unitGridPosition, targetGridPosition)) break;

    // I moved this from below this 'if' _into_ this if
    if(!revealedGridPositionList.Contains(targetGridPosition))
    {
        revealedGridPositionList.Add(targetGridPosition);
    }
}
1 Like

Hey bixarrio, I managed to fix the issue, for whatever reason I had to recreate the FogOfWarVisualSingle prefab. After that, it covered the grid tiles. I messed with your solution and I wasn’t getting an invalid grid position error at any point. Thanks for the help!

Hello, now that I’ve got my fog of war to show up on the screen, it won’t disappear. I believe I’ve narrowed it down to the UpdateFogOfWar() on the FogOfWar.cs. Its like the UpdateFogOfWar() gets called, enables all of the fog positions, then does nothing else. It’s not even accepting the square my friendly units are standing on.

If anyone has any suggestions I would be very grateful.

Update, I’ve narrowed it down even further, it appears as though my List<GridPosition> revealedGridPositionList = new List<GridPosition>(); is not adding any of the grid tiles that I tell it to in the FogOfWar.cs script.

I’ve figured out my issue with the grid tiles not updating! The issue was the script execution order, the fog of war script was happening before the UnitManager script so there were no friendly units to call from. After fixing that the fog tiles properly disappeared. Now I’m having to figure out why the tiles don’t update as the player moves. But I’m making progress.

1 Like

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