using Unity.Netcode;
using UnityEngine;
namespace Northbound
{
///
/// 전장의 안개 시각적 렌더링 (클라이언트)
///
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class FogOfWarRenderer : MonoBehaviour
{
[Header("Settings")]
public Material fogMaterial;
public float updateInterval = 0.1f;
public float fogHeight = 5f; // 안개 높이 (지형보다 약간 위)
[Header("Smoothing")]
[Tooltip("Enable smooth circular vision edges")]
public bool enableSmoothing = true;
[Tooltip("Smoothing strength (higher = smoother but more blurry)")]
[Range(1, 5)]
public int smoothingPasses = 2;
[Header("Colors")]
public Color unexploredColor = new Color(0, 0, 0, 1f); // 완전히 어두움
public Color exploredColor = new Color(0, 0, 0, 0.6f); // 반투명
public Color visibleColor = new Color(0, 0, 0, 0f); // 투명
private Texture2D _fogTexture;
private Color[] _colors;
private MeshRenderer _meshRenderer;
private float _updateTimer;
private ulong _localClientId;
private bool _isInitialized;
private void Start()
{
// NetworkManager 준비 대기
if (NetworkManager.Singleton == null || !NetworkManager.Singleton.IsClient)
{
Debug.LogWarning("[FogOfWar] NetworkManager가 준비되지 않았습니다. 대기 중...");
return;
}
Initialize();
}
private void Initialize()
{
if (_isInitialized) return;
_localClientId = NetworkManager.Singleton.LocalClientId;
var fogSystem = FogOfWarSystem.Instance;
if (fogSystem == null)
{
Debug.LogError("[FogOfWar] FogOfWarSystem을 찾을 수 없습니다! 씬에 FogOfWarSystem GameObject가 있는지 확인하세요.");
return;
}
// 텍스처 생성 (Bilinear filtering for smooth edges)
_fogTexture = new Texture2D(fogSystem.gridWidth, fogSystem.gridHeight)
{
filterMode = FilterMode.Bilinear,
wrapMode = TextureWrapMode.Clamp,
anisoLevel = 0 // Disable anisotropic filtering for fog overlay
};
_colors = new Color[fogSystem.gridWidth * fogSystem.gridHeight];
// 초기 색상을 미탐색 상태로 설정
for (int i = 0; i < _colors.Length; i++)
{
_colors[i] = unexploredColor;
}
_fogTexture.SetPixels(_colors);
_fogTexture.Apply();
// 머티리얼 설정
_meshRenderer = GetComponent();
if (fogMaterial != null)
{
Material instanceMaterial = new Material(fogMaterial);
instanceMaterial.mainTexture = _fogTexture;
_meshRenderer.material = instanceMaterial;
}
else
{
Debug.LogWarning("[FogOfWar] Fog Material이 설정되지 않았습니다!");
}
#if UNITY_EDITOR
if (fogSystem.disableInEditor)
{
_meshRenderer.enabled = false;
}
#endif
// 렌더링 레이어 설정
gameObject.layer = LayerMask.NameToLayer("UI"); // 또는 별도 레이어 생성
// 메쉬 생성
CreatePlaneMesh(fogSystem);
_isInitialized = true;
Debug.Log($"[FogOfWar] 렌더러 초기화 완료 (Client {_localClientId})");
}
private void Update()
{
// NetworkManager가 준비되지 않았다면 초기화 시도
if (!_isInitialized && NetworkManager.Singleton != null && NetworkManager.Singleton.IsClient)
{
Initialize();
return;
}
if (!_isInitialized) return;
_updateTimer += Time.deltaTime;
if (_updateTimer >= updateInterval)
{
_updateTimer = 0f;
UpdateFogTexture();
}
}
private void UpdateFogTexture()
{
var fogSystem = FogOfWarSystem.Instance;
if (fogSystem == null) return;
#if UNITY_EDITOR
if (fogSystem.disableInEditor)
{
if (_meshRenderer != null)
_meshRenderer.enabled = false;
return;
}
#endif
if (_meshRenderer != null)
_meshRenderer.enabled = true;
var fogData = fogSystem.GetPlayerFogData(_localClientId);
for (int y = 0; y < fogSystem.gridHeight; y++)
{
for (int x = 0; x < fogSystem.gridWidth; x++)
{
FogOfWarState state = fogData.GetState(x, y);
int index = y * fogSystem.gridWidth + x;
_colors[index] = state switch
{
FogOfWarState.Visible => visibleColor,
FogOfWarState.Explored => exploredColor,
FogOfWarState.Unexplored => unexploredColor,
_ => unexploredColor
};
}
}
// Apply smoothing if enabled
if (enableSmoothing)
{
SmoothFogTexture(fogSystem.gridWidth, fogSystem.gridHeight);
}
_fogTexture.SetPixels(_colors);
_fogTexture.Apply();
}
///
/// Apply box blur smoothing to create smooth circular vision edges
///
private void SmoothFogTexture(int width, int height)
{
Color[] smoothed = new Color[_colors.Length];
for (int pass = 0; pass < smoothingPasses; pass++)
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int index = y * width + x;
// Box blur: average with neighbors
float r = 0, g = 0, b = 0, a = 0;
int samples = 0;
// Sample 3x3 kernel
for (int dy = -1; dy <= 1; dy++)
{
for (int dx = -1; dx <= 1; dx++)
{
int nx = x + dx;
int ny = y + dy;
if (nx >= 0 && nx < width && ny >= 0 && ny < height)
{
int nIndex = ny * width + nx;
Color c = _colors[nIndex];
r += c.r;
g += c.g;
b += c.b;
a += c.a;
samples++;
}
}
}
// Average
smoothed[index] = new Color(r / samples, g / samples, b / samples, a / samples);
}
}
// Copy smoothed back to colors for next pass
System.Array.Copy(smoothed, _colors, _colors.Length);
}
}
private void CreatePlaneMesh(FogOfWarSystem fogSystem)
{
MeshFilter meshFilter = GetComponent();
float width = fogSystem.gridWidth * fogSystem.cellSize;
float height = fogSystem.gridHeight * fogSystem.cellSize;
Mesh mesh = new Mesh
{
name = "FogOfWar Plane"
};
mesh.vertices = new Vector3[]
{
new Vector3(0, fogHeight, 0),
new Vector3(width, fogHeight, 0),
new Vector3(0, fogHeight, height),
new Vector3(width, fogHeight, height)
};
mesh.triangles = new int[] { 0, 2, 1, 2, 3, 1 };
mesh.uv = new Vector2[]
{
new Vector2(0, 0),
new Vector2(1, 0),
new Vector2(0, 1),
new Vector2(1, 1)
};
mesh.RecalculateNormals();
meshFilter.mesh = mesh;
transform.position = fogSystem.worldOrigin;
Debug.Log($"[FogOfWar] Plane Mesh 생성 완료: {width}x{height} at {fogSystem.worldOrigin}");
}
private void OnDestroy()
{
if (_fogTexture != null)
{
Destroy(_fogTexture);
}
}
}
}