Hello I'm having some trouble figuring out the best way to implement Fog of War in my project. My games maps are structured in a grid of 1 unit x 1 unit cells. I've created a system that instantiates a prefab object in each cell. This prefab contains a sprite (black 2 unit x 2 unit circle with faded edges), a CircleCollider2D with isTrigger set to true, and a Script that contains state data for the tile (Visible, Explored, Hidden), an Update, OnTriggerStay2D, and OnTriggerExit2D methods. OnTriggerStay2D checks for collisions of any collider2D with the "Unit" tag and if one occurs sets the state of the tile to Visible. OnTriggerExit2D sets the state to Explored. The Update method uses Color.Lerp to change the color of the sprite depending on the state. So Visible tiles are 100% transparent, Explored Tiles are 50% transparent, and Hidden Tiles are 0% transparent. That all works and looks great, but im noticing there's thousands of draw calls every frame, and the game is starting to perform poorly. I've done some googling about this issue and it seems like the most common advice given is to use shaders to manipulate the vertex data of a plane that is placed over the map. I have zero experience with shaders and have literally no clue where to start exploring this option. Out of all the stuff I've read there was never an example given as to how this can be accomplished. I'm curious if there is a way for me to optimize my current system to reduce draw calls and do things a little more efficient, or if anyone could help me with the whole vertex color shader business. Any help will be greatly appreciated. Thanks for reading! ♥
So every update it sets the colour of every sprite? I don't know if it would make a difference but each time the colour was needed to change you could add the sprite to a list then loop through & change the colour & at the end clear the list. That way it should only update the ones needed & ignore the rest.
The code currently only transitions the Color of the tile when a unit stays in or exits the tiles CircleCollider2D.
I'd advise looking into procedural mesh generation and vertex shading. A lot more difficult to implement than basic prefabs, unfortunately, but it should run better.
I honestly have no idea where to begin with something like that. Could you possibly point me in the right direction?
few code examples here, http://forum.unity3d.com/threads/fog-of-war-mesh.39837/ using mesh.colors to set mesh vertex colors : http://docs.unity3d.com/ScriptReference/Mesh-colors.html And can use CreatePlane to make highresolutioe mesh plane : http://wiki.unity3d.com/index.php/CreatePlane
Thanks I've (of course) run into a problem... The vertex colors are not actually changing... I used the code example in that other thread to raycast and detect the closest vertices in a radius around the player and then set the alpha of those vertex to 0. This is what that looks like : Code (CSharp): using UnityEngine; using System.Collections; public class UnitVision : MonoBehaviour { public LayerMask layerMask; public float radius = 1.0f; void Update() { Ray ray = new Ray(transform.position, -Vector3.forward); RaycastHit[] hits = Physics.RaycastAll(ray, 500, layerMask); Debug.DrawRay(ray.origin, ray.direction * 500, Color.red); for (var i = 0; i < hits.Length; i++) { RaycastHit hit = hits[i]; MeshFilter filter = hit.collider.GetComponent<MeshFilter>(); Vector3 relativePoint; if (filter) { relativePoint = filter.transform.InverseTransformPoint(hit.point); FullMesh(filter.mesh, relativePoint, radius); } } } void FullMesh(Mesh mesh, Vector3 position, float inRadius) { Vector3[] vertices = mesh.vertices; Vector3[] normals = mesh.normals; float sqrRadius = inRadius * inRadius; Color[] colours = mesh.colors; for (int i = 0; i < vertices.Length; i++) { //getting the vertices around the local point passed var sqrMagnitude = (vertices[i] - position).sqrMagnitude; //if the vertex is too far away, dont carry on if (sqrMagnitude > sqrRadius) continue; //set the alpha to 0 colours[i].a = 0; } mesh.colors = colours; } } I've debugged the code and it all seems to be working right. The raycasts are hitting the mesh and at the right position. Im creating the Mesh with the CreatePlane code on the wiki. The mesh containing GameObject has this script attached to it : Code (CSharp): using UnityEngine; using System.Collections; public class FogMesh : MonoBehaviour { void Start() { //getting the mesh Mesh mesh = GetComponent<MeshFilter>().mesh; Vector3[] vertices = mesh.vertices; Color[] colors = new Color[vertices.Length]; //have to filter out whatever game object its attatched too, to make sure the colours have //been set correctly before they get altered later, stops invalid array errors where colours //return null for (int i = 0; i < vertices.Length; i++) { colors[i] = Color.black; colors[i].a = 1; } mesh.colors = colors; } } The mesh has a Material with the standard shader. I've tried messing around with different shaders and textures and nothing seems to work. Any ideas?
Well I got it working. Had to use a Particle Shader. ... One more question. How could I smooth out the edges of the revealed area? It looks really nice with a blur applied, but I can't seem to get the blur to only effect the fog of war mesh.
yes, most shaders wont display vertex colors. For smoother effect, instead of setting the color straight to 0, could slowly decrease it from 1. tested something similar while ago: http://unitycoder.com/upload/demos/2DFogOfWar/ (webplayer)
I experimented with smoothing out the color transition using Color.Lerp like I was doing with the Tile Prefab system. It's an improvement but once its clear the edges are very jagged and uneven, it just doesn't look like Fog... Your web demo looks a lot better than what I've currently got going. Is there no way to apply a blur effect to a single object in the scene?