전장의 안개 기능 개선
미탐험 구역의 모든 오브젝트는 보이지 않음 적이 시야를 제공하는 문제 수정 높은 장애물은 더 먼 거리에서부터 보일 수 있음
This commit is contained in:
349
Assets/Scripts/FogOfWarVisibility.cs
Normal file
349
Assets/Scripts/FogOfWarVisibility.cs
Normal file
@@ -0,0 +1,349 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// Controls object visibility based on fog of war state
|
||||
/// Attach to buildings, obstacles, enemies, or any object that should be hidden in fog
|
||||
/// </summary>
|
||||
public class FogOfWarVisibility : MonoBehaviour
|
||||
{
|
||||
[Header("Visibility Settings")]
|
||||
[Tooltip("Show this object in explored areas (greyed out) or only when visible")]
|
||||
public bool showInExploredAreas = false;
|
||||
|
||||
[Tooltip("Update frequency for checking fog state (seconds)")]
|
||||
public float updateInterval = 0.2f;
|
||||
|
||||
[Tooltip("Renderers to show/hide (auto-detected if empty)")]
|
||||
public Renderer[] renderers;
|
||||
|
||||
[Tooltip("Enable debug logging for this object")]
|
||||
public bool debugLogging = false;
|
||||
|
||||
[Header("Height-Based Distant Visibility")]
|
||||
[Tooltip("Enable visibility from farther away based on object height")]
|
||||
public bool enableDistantVisibility = true;
|
||||
|
||||
[Tooltip("Visibility range multiplier per unit of height (default: 2x vision per 1m height)")]
|
||||
public float heightVisibilityMultiplier = 2.0f;
|
||||
|
||||
[Tooltip("Minimum height to get extended visibility (meters)")]
|
||||
public float minHeightForDistantVisibility = 3.0f;
|
||||
|
||||
[Header("Explored State Visual (Optional)")]
|
||||
[Tooltip("Apply grey/desaturated material when in explored state")]
|
||||
public bool useExploredMaterial = false;
|
||||
|
||||
[Tooltip("Material to use in explored state (optional)")]
|
||||
public Material exploredMaterial;
|
||||
|
||||
private Material[] _originalMaterials;
|
||||
private bool _isVisible = false;
|
||||
private float _updateTimer;
|
||||
private ulong _localClientId;
|
||||
private bool _isInitialized = false;
|
||||
private float _objectHeight = 0f;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// Auto-detect renderers if not set
|
||||
if (renderers == null || renderers.Length == 0)
|
||||
{
|
||||
renderers = GetComponentsInChildren<Renderer>();
|
||||
|
||||
if (debugLogging)
|
||||
{
|
||||
Debug.Log($"[FogOfWarVisibility] {gameObject.name}: Auto-detected {renderers.Length} renderers");
|
||||
}
|
||||
}
|
||||
|
||||
if (renderers == null || renderers.Length == 0)
|
||||
{
|
||||
Debug.LogWarning($"[FogOfWarVisibility] {gameObject.name}: No renderers found! Component will not work.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Store original materials for explored state
|
||||
if (useExploredMaterial && renderers != null && renderers.Length > 0)
|
||||
{
|
||||
_originalMaterials = new Material[renderers.Length];
|
||||
for (int i = 0; i < renderers.Length; i++)
|
||||
{
|
||||
if (renderers[i] != null)
|
||||
{
|
||||
_originalMaterials[i] = renderers[i].sharedMaterial;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate object height for distant visibility
|
||||
_objectHeight = CalculateObjectHeight();
|
||||
|
||||
if (debugLogging)
|
||||
{
|
||||
Debug.Log($"[FogOfWarVisibility] {gameObject.name}: Object height = {_objectHeight}m");
|
||||
}
|
||||
|
||||
// CRITICAL: Start hidden and stay hidden until fog system confirms visibility
|
||||
if (debugLogging)
|
||||
{
|
||||
Debug.Log($"[FogOfWarVisibility] {gameObject.name}: START - Setting all {renderers.Length} renderers to HIDDEN");
|
||||
}
|
||||
|
||||
// Force initial hide - don't use SetVisible because _isVisible defaults to false
|
||||
// which would cause early return
|
||||
_isVisible = true; // Set to true first so SetVisible(false) actually runs
|
||||
SetVisible(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the height of this object for distant visibility
|
||||
/// </summary>
|
||||
private float CalculateObjectHeight()
|
||||
{
|
||||
// Try to get height from Building component
|
||||
var building = GetComponent<Building>();
|
||||
if (building != null && building.buildingData != null)
|
||||
{
|
||||
return building.buildingData.height;
|
||||
}
|
||||
|
||||
// Fallback: Use renderer bounds
|
||||
if (renderers != null && renderers.Length > 0)
|
||||
{
|
||||
float maxHeight = 0f;
|
||||
foreach (var renderer in renderers)
|
||||
{
|
||||
if (renderer != null)
|
||||
{
|
||||
maxHeight = Mathf.Max(maxHeight, renderer.bounds.size.y);
|
||||
}
|
||||
}
|
||||
return maxHeight;
|
||||
}
|
||||
|
||||
// Last resort: Use collider bounds
|
||||
var collider = GetComponent<Collider>();
|
||||
if (collider != null)
|
||||
{
|
||||
return collider.bounds.size.y;
|
||||
}
|
||||
|
||||
return 0f;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// Only run on clients, not on dedicated server
|
||||
if (NetworkManager.Singleton != null && NetworkManager.Singleton.IsServer && !NetworkManager.Singleton.IsClient)
|
||||
{
|
||||
// Dedicated server - don't process visibility
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize when network is ready
|
||||
if (!_isInitialized)
|
||||
{
|
||||
if (NetworkManager.Singleton != null && NetworkManager.Singleton.IsClient)
|
||||
{
|
||||
_localClientId = NetworkManager.Singleton.LocalClientId;
|
||||
_isInitialized = true;
|
||||
|
||||
// Force immediate visibility update on initialization
|
||||
UpdateVisibility();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Network not ready - stay hidden
|
||||
SetVisible(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_updateTimer += Time.deltaTime;
|
||||
if (_updateTimer >= updateInterval)
|
||||
{
|
||||
_updateTimer = 0f;
|
||||
UpdateVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateVisibility()
|
||||
{
|
||||
var fogSystem = FogOfWarSystem.Instance;
|
||||
if (fogSystem == null)
|
||||
{
|
||||
// No fog system - stay hidden for safety
|
||||
SetVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for fog data to be initialized
|
||||
var fogData = fogSystem.GetPlayerFogData(_localClientId);
|
||||
if (fogData == null)
|
||||
{
|
||||
// Fog data not ready yet - stay hidden
|
||||
SetVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
FogOfWarState fogState = fogSystem.GetVisibilityState(_localClientId, transform.position);
|
||||
|
||||
// Check for distant visibility based on height
|
||||
bool isDistantVisible = false;
|
||||
if (enableDistantVisibility && _objectHeight >= minHeightForDistantVisibility)
|
||||
{
|
||||
isDistantVisible = CheckDistantVisibility(fogSystem);
|
||||
}
|
||||
|
||||
if (debugLogging)
|
||||
{
|
||||
Debug.Log($"[FogOfWarVisibility] {gameObject.name} at {transform.position}: State={fogState}, DistantVisible={isDistantVisible}, Height={_objectHeight}m");
|
||||
}
|
||||
|
||||
switch (fogState)
|
||||
{
|
||||
case FogOfWarState.Visible:
|
||||
// Currently visible - show with original materials
|
||||
if (debugLogging) Debug.Log($"[FogOfWarVisibility] {gameObject.name}: Setting VISIBLE");
|
||||
SetVisible(true);
|
||||
SetExploredVisual(false);
|
||||
break;
|
||||
|
||||
case FogOfWarState.Explored:
|
||||
// Previously seen but not currently visible
|
||||
// BUT: Tall objects can be seen from farther away
|
||||
if (showInExploredAreas || isDistantVisible)
|
||||
{
|
||||
if (debugLogging) Debug.Log($"[FogOfWarVisibility] {gameObject.name}: Setting EXPLORED (visible) - distantVisible={isDistantVisible}");
|
||||
SetVisible(true);
|
||||
SetExploredVisual(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (debugLogging) Debug.Log($"[FogOfWarVisibility] {gameObject.name}: Setting EXPLORED (hidden)");
|
||||
SetVisible(false);
|
||||
}
|
||||
break;
|
||||
|
||||
case FogOfWarState.Unexplored:
|
||||
// Never seen - hide unless tall enough to see from distance
|
||||
if (isDistantVisible)
|
||||
{
|
||||
if (debugLogging) Debug.Log($"[FogOfWarVisibility] {gameObject.name}: Setting UNEXPLORED but DISTANT VISIBLE");
|
||||
SetVisible(true);
|
||||
SetExploredVisual(true); // Show as explored/distant
|
||||
}
|
||||
else
|
||||
{
|
||||
if (debugLogging) Debug.Log($"[FogOfWarVisibility] {gameObject.name}: Setting UNEXPLORED (hidden)");
|
||||
SetVisible(false);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Unknown state - hide completely
|
||||
SetVisible(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if this object should be visible from a distance based on its height
|
||||
/// </summary>
|
||||
private bool CheckDistantVisibility(FogOfWarSystem fogSystem)
|
||||
{
|
||||
// Find the closest vision provider (player)
|
||||
if (NetworkManager.Singleton == null || !NetworkManager.Singleton.IsClient)
|
||||
return false;
|
||||
|
||||
// Get local player object
|
||||
var localPlayer = NetworkManager.Singleton.LocalClient?.PlayerObject;
|
||||
if (localPlayer == null)
|
||||
return false;
|
||||
|
||||
// Calculate distance to player
|
||||
float distanceToPlayer = Vector3.Distance(transform.position, localPlayer.transform.position);
|
||||
|
||||
// Calculate extended visibility range based on height
|
||||
// Taller objects can be seen from farther away
|
||||
// Formula: Base range + (height - minHeight) * multiplier
|
||||
float extendedRange = (_objectHeight - minHeightForDistantVisibility) * heightVisibilityMultiplier;
|
||||
|
||||
// Get player's vision range (assume average vision provider has ~15 unit range)
|
||||
float baseVisionRange = 15f; // You can make this configurable
|
||||
float totalRange = baseVisionRange + extendedRange;
|
||||
|
||||
if (debugLogging)
|
||||
{
|
||||
Debug.Log($"[FogOfWarVisibility] {gameObject.name}: Distance={distanceToPlayer:F1}m, ExtendedRange={totalRange:F1}m (height bonus: +{extendedRange:F1}m)");
|
||||
}
|
||||
|
||||
return distanceToPlayer <= totalRange;
|
||||
}
|
||||
|
||||
private void SetVisible(bool visible)
|
||||
{
|
||||
if (_isVisible == visible) return;
|
||||
|
||||
_isVisible = visible;
|
||||
|
||||
if (renderers == null || renderers.Length == 0)
|
||||
{
|
||||
if (debugLogging)
|
||||
{
|
||||
Debug.LogWarning($"[FogOfWarVisibility] {gameObject.name}: SetVisible({visible}) called but no renderers!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (debugLogging)
|
||||
{
|
||||
Debug.Log($"[FogOfWarVisibility] {gameObject.name}: SetVisible({visible}) - updating {renderers.Length} renderers");
|
||||
}
|
||||
|
||||
foreach (var renderer in renderers)
|
||||
{
|
||||
if (renderer != null)
|
||||
{
|
||||
renderer.enabled = visible;
|
||||
|
||||
if (debugLogging)
|
||||
{
|
||||
Debug.Log($"[FogOfWarVisibility] {gameObject.name}: Renderer '{renderer.name}' enabled = {visible}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetExploredVisual(bool explored)
|
||||
{
|
||||
if (!useExploredMaterial || renderers == null || exploredMaterial == null)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < renderers.Length; i++)
|
||||
{
|
||||
if (renderers[i] != null)
|
||||
{
|
||||
if (explored)
|
||||
{
|
||||
renderers[i].material = exploredMaterial;
|
||||
}
|
||||
else if (_originalMaterials != null && i < _originalMaterials.Length)
|
||||
{
|
||||
renderers[i].material = _originalMaterials[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
// Visualize fog state check position
|
||||
Gizmos.color = _isVisible ? Color.green : Color.red;
|
||||
Gizmos.DrawWireSphere(transform.position, 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user