Project_WL/Assets/ResWork/DynamicFog/Scripts/DynamicFogOfWar.cs

179 lines
6.8 KiB
C#

using UnityEngine;
using System.Collections;
namespace DynamicFogAndMist {
[ExecuteInEditMode]
public class DynamicFogOfWar : MonoBehaviour {
public int fogOfWarTextureSize = 512;
Material fogMat;
static DynamicFogOfWar _instance;
Texture2D fogOfWarTexture;
Color32[] fogOfWarColorBuffer;
public static DynamicFogOfWar instance {
get {
if (_instance == null) {
_instance = FindObjectOfType<DynamicFogOfWar>();
}
return _instance;
}
}
void OnEnable() {
fogMat = GetComponent<MeshRenderer>().sharedMaterial;
UpdateFogOfWarTexture();
}
void OnDisable() {
if (fogOfWarTexture != null) {
DestroyImmediate(fogOfWarTexture);
fogOfWarTexture = null;
}
}
void Update() {
fogMat.SetVector("_FogOfWarData", new Vector4(transform.position.x, transform.position.z, transform.localScale.x, transform.localScale.y));
}
void UpdateFogOfWarTexture() {
int size = GetScaledSize(fogOfWarTextureSize, 1.0f);
fogOfWarTexture = new Texture2D(size, size, TextureFormat.ARGB32, false);
fogOfWarTexture.hideFlags = HideFlags.DontSave;
fogOfWarTexture.filterMode = FilterMode.Bilinear;
fogOfWarTexture.wrapMode = TextureWrapMode.Clamp;
fogMat.mainTexture = fogOfWarTexture;
ResetFogOfWar();
}
int GetScaledSize(int size, float factor) {
size = (int)(size / factor);
size /= 4;
if (size < 1)
size = 1;
return size * 4;
}
#region Fog of War Public API
/// <summary>
/// Returns the alpha value of fog of war at a given world space position
/// </summary>
/// <param name="worldPosition">in world space coordinates.</param>
public float GetFogOfWarAlpha(Vector3 worldPosition) {
if (fogOfWarTexture == null)
return 1;
float tx = (worldPosition.x - transform.position.x) / transform.localScale.x + 0.5f;
if (tx < 0 || tx > 1f)
return 1;
float tz = (worldPosition.z - transform.position.z) / transform.localScale.y + 0.5f;
if (tz < 0 || tz > 1f)
return 1;
int tw = fogOfWarTexture.width;
int th = fogOfWarTexture.height;
int px = (int)(tx * tw);
int pz = (int)(tz * th);
int colorBufferPos = pz * tw + px;
return fogOfWarColorBuffer[colorBufferPos].a / 255f;
}
/// <summary>
/// Changes the alpha value of the fog of war at world position. It takes into account FogOfWarCenter and FogOfWarSize.
/// Note that only x and z coordinates are used. Y (vertical) coordinate is ignored.
/// </summary>
/// <param name="worldPosition">in world space coordinates.</param>
/// <param name="radius">radius of application in world units.</param>
public void SetFogOfWarAlpha(Vector3 worldPosition, float radius, float fogNewAlpha) {
if (fogOfWarTexture == null)
return;
float tx = (worldPosition.x - transform.position.x) / transform.localScale.x + 0.5f;
if (tx < 0 || tx > 1f)
return;
float tz = (worldPosition.z - transform.position.z) / transform.localScale.y + 0.5f;
if (tz < 0 || tz > 1f)
return;
int tw = fogOfWarTexture.width;
int th = fogOfWarTexture.height;
int px = (int)(tx * tw);
int pz = (int)(tz * th);
int colorBufferPos = pz * tw + px;
byte newAlpha8 = (byte)(fogNewAlpha * 255);
Color32 existingColor = fogOfWarColorBuffer[colorBufferPos];
if (newAlpha8 != existingColor.a) { // just to avoid over setting the texture in an Update() loop
float tr = radius / transform.localScale.y;
int delta = Mathf.FloorToInt(th * tr);
for (int r = pz - delta; r <= pz + delta; r++) {
if (r >= 0 && r < th) {
for (int c = px - delta; c <= px + delta; c++) {
if (c >= 0 && c < tw) {
int distance = (int)(Mathf.Sqrt((pz - r) * (pz - r) + (px - c) * (px - c)));
if (distance <= delta) {
colorBufferPos = r * tw + c;
Color32 colorBuffer = fogOfWarColorBuffer[colorBufferPos];
colorBuffer.a = (byte)Mathf.Lerp(newAlpha8, colorBuffer.a, (float)distance / delta);
fogOfWarColorBuffer[colorBufferPos] = colorBuffer;
fogOfWarTexture.SetPixel(c, r, colorBuffer);
}
}
}
}
}
fogOfWarTexture.Apply();
}
}
public void ResetFogOfWar() {
if (fogOfWarTexture == null)
return;
int h = fogOfWarTexture.height;
int w = fogOfWarTexture.width;
int newLength = h * w;
if (fogOfWarColorBuffer == null || fogOfWarColorBuffer.Length != newLength) {
fogOfWarColorBuffer = new Color32[newLength];
}
Color32 opaque = new Color32(255, 255, 255, 255);
for (int k = 0; k < newLength; k++)
fogOfWarColorBuffer[k] = opaque;
fogOfWarTexture.SetPixels32(fogOfWarColorBuffer);
fogOfWarTexture.Apply();
}
public void SetFogOfWarTerrainBoundary(Terrain terrain, float borderWidth) {
TerrainData td = terrain.terrainData;
int tw = td.heightmapResolution;
int th = td.heightmapResolution;
float ta = td.size.y;
float[,] heights = td.GetHeights(0, 0, tw, th);
float y0 = transform.position.y - 1f;
float y1 = transform.position.y + 10f;
//Vector3 halfSize = new Vector3(-td.size.x * 0.5f, 0, -td.size.z * 0.5f);
Vector3 terrainPos = terrain.GetPosition();
for (int j = 0; j < th; j++) {
for (int k = 0; k < tw; k++) {
float h = heights[j, k] * ta + terrain.transform.position.y;
if (h > y0 && h < y1) {
Vector3 wpos = terrainPos + new Vector3(td.size.x * (k + 0.5f) / tw, 0, td.size.z * (j + 0.5f) / th);
SetFogOfWarAlpha(wpos, borderWidth, 0);
}
}
}
}
#endregion
}
}