Implementing Fog Of War In Unity

Attach the below Script to an empty game object, and then apply the shader below to the material. I have created a plane from scratch for this to work as desired and easy to customize. The script that creates plane is also posted here.

The code is not explained in detail, if you have any difficulties please comment….

 using UnityEngine;  
 [RequireComponent (typeof(MeshFilter), typeof(MeshRenderer))]  
 public class FogOfWar : MonoBehaviour   
 {  
   Transform _player;  
   CustomPlane _plane;  
   Mesh _mesh;  
   public int explorerRangeX;  
   public int explorerRangeY;  
   TVec2<int> _lastExploredIndex;  
   void Awake()  
   {  
     _player = GameObject.FindGameObjectWithTag("Player").transform;  
     _mesh = GetComponent<MeshFilter>().mesh;  
     _mesh.Clear();  
     Events.instance.AddListener<GameEvent>(GenerateFogOfWar);  
   }  
   void GenerateFogOfWar(GameEvent e)  
   {  
     if (e.type == GameEvent.GENERATE_FOW)  
     {  
       Events.instance.RemoveListener<GameEvent>(GenerateFogOfWar);  
       _plane = new CustomPlane(e.width * e.gridSize, e.height * e.gridSize, (e.width * 8 / 512));  
       _mesh.vertices = _plane.getVertices();  
       _mesh.triangles = _plane.getTriangles();  
       _mesh.uv = _plane.getUVs();  
       _mesh.colors32 = _plane.getColors();  
       gameObject.transform.Translate(new Vector3(-_plane.width / 2, 0, -_plane.height / 2));  
       MeshCollider collider = gameObject.AddComponent<MeshCollider>();  
       collider.sharedMesh = _mesh;  
       _lastExploredIndex = new TVec2<int>(-1, -1);  
     }  
   }  
   void OnRenderObject()  
   {  
     Ray ray = new Ray(_player.position + Vector3.up * 20.0f, Vector3.down);  
     RaycastHit hitInfo;  
     if (Physics.Raycast(ray, out hitInfo, 5.0f))  
     {  
       Explore(gameObject.transform.InverseTransformPoint(hitInfo.point));  
     }  
   }  
   void UpdatePolygonColorAtIndex(int indexX, int indexY, int rangeX, int rangeY, byte alpha, bool isBorder = false)  
   {  
     for (int x = -rangeX; x <= rangeX; x++)  
     {  
       for (int y = -rangeY; y <= rangeY; y++)  
       {  
         if (isBorder)  
         {  
           if (x != -rangeX && x != rangeX && y != -rangeY && y != rangeY)  
           {  
             continue;  
           }  
         }  
         if (  
           _plane.isWithinRange((indexX + x), (indexY + y)) &&  
           _plane[indexX+x, indexY+y].getAlpha() > alpha)  
         {  
           _plane.UpdatePolygonColorAtIndex(indexX + x, indexY + y, new Color32(0, 0, 0, alpha));  
         }  
       }  
     }  
   }  
   void Explore(Vector3 position)  
   {  
     Vector2 quadIndex = _plane.GetPolygonIndexFromPosition(position);  
     int indexX = (int)quadIndex.x;  
     int indexY = (int)quadIndex.y;  
     if (_lastExploredIndex.x == indexX && _lastExploredIndex.y == indexY)  
     {  
       //don't update  
     }  
     else  
     {  
       UpdatePolygonColorAtIndex(indexX, indexY, explorerRangeX + 1, explorerRangeY + 1, 64, true);  
       UpdatePolygonColorAtIndex(indexX, indexY, explorerRangeX, explorerRangeY, 0, false);  
       UpdatePolygonColorAtIndex(indexX, indexY, explorerRangeX + 3, explorerRangeY, 64, true);  
       UpdatePolygonColorAtIndex(indexX, indexY, explorerRangeX + 2, explorerRangeY - 1, 0, false);  
       UpdatePolygonColorAtIndex(indexX, indexY, explorerRangeX, explorerRangeY + 2, 64, true);  
       UpdatePolygonColorAtIndex(indexX, indexY, explorerRangeX - 1, explorerRangeY + 1, 0, false);  
       _mesh.colors32 = _plane.getColors();  
       _lastExploredIndex.Set(indexX, indexY);  
     }  
   }  
 }  

Shader Code
:

 Shader "Custom/VertexAlphaFow"  
 {  
      Properties  
      {  
           _Color("Color", Color) = (1,1,1,1)  
           _MainTex("Base Albedo (RGB)", 2D) = "white" {}  
      }  
      SubShader  
      {  
           Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }  
           Blend SrcAlpha OneMinusSrcAlpha  
           pass  
           {  
                CGPROGRAM  
                #pragma fragmentoption ARB_precision_hint_fastest  
                #pragma vertex vert  
                #pragma fragment frag  
                #include "UnityCG.cginc"  
                #pragma target 3.0  
                uniform sampler2D _MainTex;  
                struct Vertex  
                {  
                     float4 vertex : POSITION;  
                     float4 color : COLOR;  
                     float4 texCoord : TEXCOORD0;  
                };  
                struct Fragment  
                {  
                     float4 pos : SV_POSITION;  
                     half4 color : COLOR;  
                     half2 uv : TEXCOORD0;  
                };  
                Fragment vert(Vertex i)  
                {  
                     Fragment fragOut;  
                     fragOut.color = i.color;  
                     fragOut.pos = mul(UNITY_MATRIX_MVP, i.vertex);  
                     fragOut.uv = i.texCoord;  
                     return fragOut;  
                }  
                half4 frag(Fragment i) : COLOR  
                {  
                     //in case want a tex  
                     //return half4(tex2D(_MainTex, i.uv).rgb, i.color.a);  
                     //use vertex color  
                     return i.color;  
                }  
                ENDCG  
           }  
      }  
           FallBack "VertexLit"  
 }  

Custom Plane Script

 using UnityEngine;  
 using System.Collections.Generic;  
 public class CustomPlane  
 {  
   private Polygon[,] _polygons;  
   public Polygon this[int x, int y]  
   {  
     get  
     {  
       return _polygons[x, y];  
     }  
   }  
   private int _quadSize;  
   public int quadSize  
   {  
     get  
     {  
       return _quadSize;  
     }  
     set  
     {  
       _quadSize = value;  
     }  
   }  
   private int _width;  
   public int width  
   {  
     get  
     {  
       return _width;  
     }  
     set  
     {  
       _width = value;  
     }  
   }  
   private int _height;  
   public int height  
   {  
     get  
     {  
       return _height;  
     }  
     set  
     {  
       _height = value;  
     }  
   }  
   private List<Vector3> _vertices;  
   private List<Vector2> _uvs;  
   private List<int> _triangles;  
   private List<Color32> _colors;  
   public CustomPlane(int width, int height, int quadSize)  
   {  
     _quadSize = quadSize;  
     _width = width;  
     _height = height;  
     _vertices = new List<Vector3>();  
     _triangles = new List<int>();  
     _uvs = new List<Vector2>();  
     _colors = new List<Color32>();  
     int row = Mathf.CeilToInt(_width / _quadSize);  
     int col = Mathf.CeilToInt(_height / _quadSize);  
     _polygons = new Polygon[row, col];  
     int runningIndex = -1;  
     for (int x = 0; x < row; x++)  
     {  
       for (int y = 0; y < col; y++)  
       {  
         int lastRunningIndex = runningIndex;  
         Vertex bottomLeft = (x - 1 > -1) ? _polygons[x - 1, y].bottomRight : (y - 1 > -1) ? _polygons[x, y - 1].topLeft : Polygon.getNode("bl", x, y, quadSize, ++runningIndex);  
         if (lastRunningIndex != runningIndex)  
         {  
           _vertices.Add(bottomLeft.position);  
         }  
         lastRunningIndex = runningIndex;  
         Vertex topLeft = (x - 1 > -1) ? _polygons[x - 1, y].topRight : Polygon.getNode("tl", x, y, quadSize, ++runningIndex);  
         if (lastRunningIndex != runningIndex)  
         {  
           _vertices.Add(topLeft.position);  
         }  
         lastRunningIndex = runningIndex;  
         Vertex topRight = Polygon.getNode("tr", x, y, quadSize, ++runningIndex);  
         if (lastRunningIndex != runningIndex)  
         {  
           _vertices.Add(topRight.position);  
         }  
         lastRunningIndex = runningIndex;  
         Vertex bottomRight = (y - 1 > -1) ? _polygons[x, y - 1].topRight : Polygon.getNode("br", x, y, quadSize, ++runningIndex);  
         if (lastRunningIndex != runningIndex)  
         {  
           _vertices.Add(bottomRight.position);  
         }  
         _polygons[x, y] = new Polygon(bottomLeft, topLeft, topRight, bottomRight, new Color32(0, 0, 0, 255));  
         _triangles.Add(bottomLeft.index);  
         _triangles.Add(topLeft.index);  
         _triangles.Add(topRight.index);  
         _triangles.Add(bottomRight.index);  
         _triangles.Add(bottomLeft.index);  
         _triangles.Add(topRight.index);  
       }  
     }  
     //generate uv's & color  
     int length = _vertices.Count;  
     for (int index = 0; index < length; index++)  
     {  
       _uvs.Add(  
             new Vector2(  
                 Mathf.InverseLerp(0, _width, _vertices[index].x),  
                 Mathf.InverseLerp(0, _height, _vertices[index].z)  
               )  
         );  
       _colors.Add(new Color32(0, 0, 0, 128));  
     }  
   }  
   public Vector3[] getVertices()  
   {  
     return _vertices.ToArray();  
   }  
   public int[] getTriangles()  
   {  
     return _triangles.ToArray();  
   }  
   public Vector2[] getUVs()  
   {  
     return _uvs.ToArray();  
   }  
   public Color32[] getColors()  
   {  
     return _colors.ToArray();  
   }  
   public Vector2 GetPolygonIndexFromTexCoord(Vector2 texCoord)  
   {  
     int xIndex, yIndex = 0;  
     xIndex = Mathf.CeilToInt((texCoord.x * _width) / _quadSize);  
     yIndex = Mathf.CeilToInt((texCoord.y * _height) / _quadSize);  
     return new Vector2(xIndex, yIndex);  
   }  
   public Vector2 GetPolygonIndexFromPosition(Vector3 position)  
   {  
     int xIndex, yIndex = 0;  
     xIndex = Mathf.FloorToInt(position.x / _quadSize);  
     yIndex = Mathf.FloorToInt(position.z / _quadSize);  
     return new Vector2(xIndex, yIndex);  
   }  
   private void UpdateColorAt(int vertexIndex, Color32 color)  
   {  
     _colors[vertexIndex] = color;  
   }  
   public bool isWithinRange(int x, int y)  
   {  
     return ((x >= 0 && x < _polygons.GetLength(0)) && (y >= 0 && y < _polygons.GetLength(1)));  
   }  
   public void UpdatePolygonColorAtIndex(int x, int y, Color32 color)  
   {  
     Polygon poly = _polygons[x, y];  
     //update polygon data  
     poly.UpdateColor(color);  
     //update render data from updated polygon  
     UpdateColorAt(poly.bottomLeft.index, color);  
     UpdateColorAt(poly.topLeft.index, color);  
     UpdateColorAt(poly.topRight.index, color);  
     UpdateColorAt(poly.bottomRight.index, color);  
   }  
 }  

Happy Coding 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *