using UnityEngine; using UnityEngine.Tilemaps; public class FogOfWar : MonoBehaviour { public Tilemap fogTilemap; public TileBase fogTile; public Transform player; public int viewRadius = 5; public Camera mainCamera; private Vector3Int lastPlayerCell; private const float UpdateInterval = 0.1f; // Update every 100ms private float updateTimer; private TileBase[,] discoveredFog; // Store the lowest opacity fog level for each tile [System.Serializable] public class FogLevel { public TileBase tile; public float opacity; } public FogLevel[] fogLevels; private void Start() { if (mainCamera == null) mainCamera = Camera.main; // Initialize the discovered fog array int fogDepth = GenerateTileMap.groundDepth - 1; // Subtract 2 to account for array starting at y=-3 discoveredFog = new TileBase[GenerateTileMap.maxWidth + 1, fogDepth]; // Initialize fog using GenerateTileMap bounds for (int x = 0; x <= GenerateTileMap.maxWidth; x++) { // Clear surface tiles (0 to -1) for (int y = 0; y >= -1; y--) { Vector3Int tilePosition = new Vector3Int(x, y, 0); fogTilemap.SetTile(tilePosition, null); } // Set fog for underground tiles (-2 and below) for (int y = -2; y >= -GenerateTileMap.groundDepth; y--) { Vector3Int tilePosition = new Vector3Int(x, y, 0); fogTilemap.SetTile(tilePosition, fogTile); // Convert to array index (y=-2 maps to array index 0) int arrayY = Mathf.Abs(y + 2); discoveredFog[x, arrayY] = fogTile; } } // Position the fog tilemap at the same position as the main tilemap fogTilemap.transform.position = new Vector3( (GenerateTileMap.maxWidth / 2) * -1, -1, transform.position.z ); lastPlayerCell = fogTilemap.WorldToCell(player.position); if (SaveSystem.isGameLoaded) { SaveDataMap mapState = GetComponent().GetMapStateFromSave(); if (mapState != null && mapState.fogOfWarData != null) { LoadFromSaveData(mapState.fogOfWarData); } } } private void Update() { updateTimer += Time.deltaTime; if (updateTimer < UpdateInterval) return; updateTimer = 0; Vector3Int playerCell = fogTilemap.WorldToCell(player.position); // Only update if player moved to a new cell if (playerCell == lastPlayerCell) return; lastPlayerCell = playerCell; // Get camera bounds in world space Vector2 cameraMin = mainCamera.ViewportToWorldPoint(new Vector3(0, 0)); Vector2 cameraMax = mainCamera.ViewportToWorldPoint(new Vector3(1, 1)); // Convert to tile coordinates Vector3Int tileMin = fogTilemap.WorldToCell(cameraMin); Vector3Int tileMax = fogTilemap.WorldToCell(cameraMax); // Clamp to map bounds tileMin.x = Mathf.Max(0, tileMin.x - viewRadius); tileMin.y = Mathf.Max(-GenerateTileMap.groundDepth, tileMin.y - viewRadius); tileMax.x = Mathf.Min(GenerateTileMap.maxWidth, tileMax.x + viewRadius); tileMax.y = Mathf.Min(-2, tileMax.y + viewRadius); // Clear fog around player // Only check tiles within camera view for (int x = tileMin.x; x <= tileMax.x; x++) { for (int y = tileMin.y; y <= tileMax.y; y++) { Vector3Int tilePosition = new Vector3Int(x, y, 0); float distance = Vector2.Distance(((Vector2Int)playerCell), (Vector2Int)tilePosition); if (distance <= viewRadius) { int fogIndex = Mathf.FloorToInt((distance / viewRadius) * (fogLevels.Length - 1)); fogIndex = Mathf.Clamp(fogIndex, 0, fogLevels.Length - 1); TileBase newFogTile = distance <= 1 ? null : fogLevels[fogIndex].tile; // Update the discovered state with the lowest opacity (null or lower fog index) int arrayY = Mathf.Abs(y + 2); // y=-2 maps to array index 0 if (x >= 0 && x < discoveredFog.GetLength(0) && arrayY >= 0 && arrayY < discoveredFog.GetLength(1)) { if (newFogTile == null) { discoveredFog[x, arrayY] = null; } else if (discoveredFog[x, arrayY] != null) { // Find the index of the current and new fog tiles int currentIndex = System.Array.FindIndex(fogLevels, f => f.tile == discoveredFog[x, arrayY]); int newIndex = System.Array.FindIndex(fogLevels, f => f.tile == newFogTile); // Keep the lower opacity version if (newIndex < currentIndex) { discoveredFog[x, arrayY] = newFogTile; } } // Always use the discovered state fogTilemap.SetTile(tilePosition, discoveredFog[x, arrayY]); } } } } } public (TileBase[,] discoveredFog, TileBase fogTile, FogLevel[] fogLevels) GetSaveValues() { return (discoveredFog,fogTile, fogLevels); } public void LoadFromSaveData(FogOfWarData saveData) { if (saveData == null || saveData.discoveredTiles == null) return; // Reset fog to full opacity for (int x = 0; x < discoveredFog.GetLength(0); x++) { // Reset underground tiles only (-2 and below) for (int y = -2; y >= -GenerateTileMap.groundDepth; y--) { Vector3Int tilePosition = new Vector3Int(x, y, 0); fogTilemap.SetTile(tilePosition, fogTile); int arrayY = Mathf.Abs(y + 2); discoveredFog[x, arrayY] = fogTile; } } // Apply saved fog states foreach (FogTileData tileData in saveData.discoveredTiles) { if (tileData.x >= 0 && tileData.x < discoveredFog.GetLength(0) && tileData.y >= 0 && tileData.y < discoveredFog.GetLength(1)) { TileBase tileToUse = tileData.fogLevelIndex == -1 ? null : fogLevels[tileData.fogLevelIndex].tile; discoveredFog[tileData.x, tileData.y] = tileToUse; // Convert array Y coordinate back to world Y coordinate int worldY = -(tileData.y + 2); // Convert array index back to world coordinates fogTilemap.SetTile(new Vector3Int(tileData.x, worldY, 0), tileToUse); } } } }