전장의 안개 구현
This commit is contained in:
48
Assets/Scripts/BuildingVisionProvider.cs
Normal file
48
Assets/Scripts/BuildingVisionProvider.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// 건물의 시야 제공 컴포넌트
|
||||
/// </summary>
|
||||
public class BuildingVisionProvider : NetworkBehaviour, IVisionProvider
|
||||
{
|
||||
[Header("Vision Settings")]
|
||||
public float visionRange = 15f;
|
||||
|
||||
private Building _building;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_building = GetComponent<Building>();
|
||||
}
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
if (IsServer)
|
||||
{
|
||||
FogOfWarSystem.Instance?.RegisterVisionProvider(this);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
if (IsServer)
|
||||
{
|
||||
FogOfWarSystem.Instance?.UnregisterVisionProvider(this);
|
||||
}
|
||||
}
|
||||
|
||||
public ulong GetOwnerId() => OwnerClientId;
|
||||
public float GetVisionRange() => visionRange;
|
||||
public Transform GetTransform() => transform;
|
||||
public bool IsActive() => IsSpawned;
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
Gizmos.color = Color.cyan;
|
||||
Gizmos.DrawWireSphere(transform.position, visionRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/BuildingVisionProvider.cs.meta
Normal file
2
Assets/Scripts/BuildingVisionProvider.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b59ae4328ce49c846b20d7a6d7ce7e47
|
||||
186
Assets/Scripts/FogOfWarRenderer.cs
Normal file
186
Assets/Scripts/FogOfWarRenderer.cs
Normal file
@@ -0,0 +1,186 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// 전장의 안개 시각적 렌더링 (클라이언트)
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
|
||||
public class FogOfWarRenderer : MonoBehaviour
|
||||
{
|
||||
[Header("Settings")]
|
||||
public Material fogMaterial;
|
||||
public float updateInterval = 0.1f;
|
||||
public float fogHeight = 5f; // 안개 높이 (지형보다 약간 위)
|
||||
|
||||
[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;
|
||||
}
|
||||
|
||||
// 텍스처 생성
|
||||
_fogTexture = new Texture2D(fogSystem.gridWidth, fogSystem.gridHeight)
|
||||
{
|
||||
filterMode = FilterMode.Bilinear,
|
||||
wrapMode = TextureWrapMode.Clamp
|
||||
};
|
||||
_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<MeshRenderer>();
|
||||
if (fogMaterial != null)
|
||||
{
|
||||
Material instanceMaterial = new Material(fogMaterial);
|
||||
instanceMaterial.mainTexture = _fogTexture;
|
||||
_meshRenderer.material = instanceMaterial;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[FogOfWar] Fog Material이 설정되지 않았습니다!");
|
||||
}
|
||||
|
||||
// 렌더링 레이어 설정
|
||||
gameObject.layer = LayerMask.NameToLayer("UI"); // 또는 별도 레이어 생성
|
||||
|
||||
// 메쉬 생성
|
||||
CreatePlaneMesh(fogSystem);
|
||||
|
||||
_isInitialized = true;
|
||||
Debug.Log($"<color=cyan>[FogOfWar] 렌더러 초기화 완료 (Client {_localClientId})</color>");
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
_fogTexture.SetPixels(_colors);
|
||||
_fogTexture.Apply();
|
||||
}
|
||||
|
||||
private void CreatePlaneMesh(FogOfWarSystem fogSystem)
|
||||
{
|
||||
MeshFilter meshFilter = GetComponent<MeshFilter>();
|
||||
|
||||
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($"<color=cyan>[FogOfWar] Plane Mesh 생성 완료: {width}x{height} at {fogSystem.worldOrigin}</color>");
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (_fogTexture != null)
|
||||
{
|
||||
Destroy(_fogTexture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/FogOfWarRenderer.cs.meta
Normal file
2
Assets/Scripts/FogOfWarRenderer.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a04555e5afcce6848ba5f06cf14a7f17
|
||||
397
Assets/Scripts/FogOfWarSystem.cs
Normal file
397
Assets/Scripts/FogOfWarSystem.cs
Normal file
@@ -0,0 +1,397 @@
|
||||
using System.Collections.Generic;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// 전장의 안개 시스템 - 플레이어별 시야 관리
|
||||
/// </summary>
|
||||
public class FogOfWarSystem : NetworkBehaviour
|
||||
{
|
||||
public static FogOfWarSystem Instance { get; private set; }
|
||||
|
||||
[Header("Grid Settings")]
|
||||
public int gridWidth = 100;
|
||||
public int gridHeight = 100;
|
||||
public float cellSize = 1f;
|
||||
public Vector3 worldOrigin = Vector3.zero;
|
||||
|
||||
[Header("Visibility Settings")]
|
||||
public float updateInterval = 0.2f;
|
||||
|
||||
// 서버: 각 플레이어별 가시성 맵
|
||||
private Dictionary<ulong, FogOfWarData> _serverFogData = new Dictionary<ulong, FogOfWarData>();
|
||||
|
||||
// 클라이언트: 로컬 플레이어의 가시성 맵
|
||||
private FogOfWarData _localFogData;
|
||||
|
||||
private List<IVisionProvider> _visionProviders = new List<IVisionProvider>();
|
||||
private float _updateTimer;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
|
||||
if (IsClient && !IsServer)
|
||||
{
|
||||
// 클라이언트는 로컬 데이터 초기화
|
||||
_localFogData = new FogOfWarData(gridWidth, gridHeight);
|
||||
Debug.Log($"<color=cyan>[FogOfWar] 클라이언트 {NetworkManager.LocalClientId} 초기화</color>");
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
|
||||
_updateTimer += Time.deltaTime;
|
||||
if (_updateTimer >= updateInterval)
|
||||
{
|
||||
_updateTimer = 0f;
|
||||
UpdateAllVision();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 시야 제공자 등록 (플레이어, 건물 등)
|
||||
/// </summary>
|
||||
public void RegisterVisionProvider(IVisionProvider provider)
|
||||
{
|
||||
if (!_visionProviders.Contains(provider))
|
||||
{
|
||||
_visionProviders.Add(provider);
|
||||
Debug.Log($"<color=cyan>[FogOfWar] Vision Provider 등록: {provider.GetTransform().name} (Owner: {provider.GetOwnerId()})</color>");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 시야 제공자 등록 해제
|
||||
/// </summary>
|
||||
public void UnregisterVisionProvider(IVisionProvider provider)
|
||||
{
|
||||
_visionProviders.Remove(provider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 플레이어별 FogOfWar 데이터 가져오기
|
||||
/// </summary>
|
||||
public FogOfWarData GetPlayerFogData(ulong clientId)
|
||||
{
|
||||
// 클라이언트는 자신의 로컬 데이터 반환
|
||||
if (IsClient && !IsServer)
|
||||
{
|
||||
return _localFogData;
|
||||
}
|
||||
|
||||
// 서버는 해당 클라이언트의 데이터 반환
|
||||
if (!_serverFogData.ContainsKey(clientId))
|
||||
{
|
||||
_serverFogData[clientId] = new FogOfWarData(gridWidth, gridHeight);
|
||||
}
|
||||
return _serverFogData[clientId];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 모든 시야 업데이트 (서버만)
|
||||
/// </summary>
|
||||
private void UpdateAllVision()
|
||||
{
|
||||
// 각 플레이어의 현재 시야 초기화
|
||||
foreach (var fogData in _serverFogData.Values)
|
||||
{
|
||||
fogData.ClearCurrentVision();
|
||||
}
|
||||
|
||||
// 모든 시야 제공자의 시야 범위 계산
|
||||
foreach (var provider in _visionProviders)
|
||||
{
|
||||
if (provider == null || !provider.IsActive()) continue;
|
||||
|
||||
ulong ownerId = provider.GetOwnerId();
|
||||
if (!_serverFogData.ContainsKey(ownerId))
|
||||
{
|
||||
_serverFogData[ownerId] = new FogOfWarData(gridWidth, gridHeight);
|
||||
}
|
||||
|
||||
Vector3 position = provider.GetTransform().position;
|
||||
float visionRange = provider.GetVisionRange();
|
||||
|
||||
RevealArea(ownerId, position, visionRange);
|
||||
}
|
||||
|
||||
// 각 클라이언트에게 시야 데이터 전송
|
||||
foreach (var kvp in _serverFogData)
|
||||
{
|
||||
ulong clientId = kvp.Key;
|
||||
FogOfWarData fogData = kvp.Value;
|
||||
|
||||
// 압축된 데이터로 전송
|
||||
byte[] visibleData = fogData.GetVisibleData();
|
||||
byte[] exploredData = fogData.GetExploredData();
|
||||
|
||||
SendFogDataToClientRpc(visibleData, exploredData,
|
||||
new ClientRpcParams
|
||||
{
|
||||
Send = new ClientRpcSendParams
|
||||
{
|
||||
TargetClientIds = new ulong[] { clientId }
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 클라이언트에게 안개 데이터 전송
|
||||
/// </summary>
|
||||
[ClientRpc]
|
||||
private void SendFogDataToClientRpc(byte[] visibleData, byte[] exploredData, ClientRpcParams clientRpcParams = default)
|
||||
{
|
||||
if (_localFogData == null)
|
||||
{
|
||||
_localFogData = new FogOfWarData(gridWidth, gridHeight);
|
||||
}
|
||||
|
||||
_localFogData.SetVisibleData(visibleData);
|
||||
_localFogData.SetExploredData(exploredData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 특정 영역을 밝힘 (서버만)
|
||||
/// </summary>
|
||||
private void RevealArea(ulong clientId, Vector3 worldPosition, float radius)
|
||||
{
|
||||
FogOfWarData fogData = _serverFogData[clientId];
|
||||
Vector2Int gridPos = WorldToGrid(worldPosition);
|
||||
|
||||
int cellRadius = Mathf.CeilToInt(radius / cellSize);
|
||||
|
||||
for (int x = -cellRadius; x <= cellRadius; x++)
|
||||
{
|
||||
for (int y = -cellRadius; y <= cellRadius; y++)
|
||||
{
|
||||
if (x * x + y * y > cellRadius * cellRadius) continue;
|
||||
|
||||
int gridX = gridPos.x + x;
|
||||
int gridY = gridPos.y + y;
|
||||
|
||||
if (gridX < 0 || gridX >= gridWidth || gridY < 0 || gridY >= gridHeight)
|
||||
continue;
|
||||
|
||||
// 현재 시야에 표시 + 방문한 적 있음으로 기록
|
||||
fogData.SetVisible(gridX, gridY, true);
|
||||
fogData.SetExplored(gridX, gridY, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 월드 좌표를 그리드 좌표로 변환
|
||||
/// </summary>
|
||||
public Vector2Int WorldToGrid(Vector3 worldPos)
|
||||
{
|
||||
Vector3 localPos = worldPos - worldOrigin;
|
||||
int x = Mathf.FloorToInt(localPos.x / cellSize);
|
||||
int z = Mathf.FloorToInt(localPos.z / cellSize);
|
||||
return new Vector2Int(x, z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 그리드 좌표를 월드 좌표로 변환
|
||||
/// </summary>
|
||||
public Vector3 GridToWorld(int x, int y)
|
||||
{
|
||||
return worldOrigin + new Vector3(x * cellSize, 0, y * cellSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 특정 위치가 플레이어에게 보이는지 확인
|
||||
/// </summary>
|
||||
public FogOfWarState GetVisibilityState(ulong clientId, Vector3 worldPosition)
|
||||
{
|
||||
FogOfWarData fogData = GetPlayerFogData(clientId);
|
||||
if (fogData == null)
|
||||
return FogOfWarState.Unexplored;
|
||||
|
||||
Vector2Int gridPos = WorldToGrid(worldPosition);
|
||||
return fogData.GetState(gridPos.x, gridPos.y);
|
||||
}
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
// 그리드 시각화
|
||||
Gizmos.color = Color.yellow;
|
||||
Vector3 size = new Vector3(gridWidth * cellSize, 0.1f, gridHeight * cellSize);
|
||||
Gizmos.DrawWireCube(worldOrigin + size / 2f, size);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 플레이어별 안개 데이터
|
||||
/// </summary>
|
||||
public class FogOfWarData
|
||||
{
|
||||
private bool[,] _explored; // 한번이라도 방문한 적이 있는가
|
||||
private bool[,] _currentlyVisible; // 현재 보이는가
|
||||
private int _width;
|
||||
private int _height;
|
||||
|
||||
public FogOfWarData(int width, int height)
|
||||
{
|
||||
_width = width;
|
||||
_height = height;
|
||||
_explored = new bool[width, height];
|
||||
_currentlyVisible = new bool[width, height];
|
||||
}
|
||||
|
||||
public void SetExplored(int x, int y, bool value)
|
||||
{
|
||||
if (IsValidCoord(x, y))
|
||||
_explored[x, y] = value;
|
||||
}
|
||||
|
||||
public void SetVisible(int x, int y, bool value)
|
||||
{
|
||||
if (IsValidCoord(x, y))
|
||||
_currentlyVisible[x, y] = value;
|
||||
}
|
||||
|
||||
public void ClearCurrentVision()
|
||||
{
|
||||
System.Array.Clear(_currentlyVisible, 0, _currentlyVisible.Length);
|
||||
}
|
||||
|
||||
public FogOfWarState GetState(int x, int y)
|
||||
{
|
||||
if (!IsValidCoord(x, y))
|
||||
return FogOfWarState.Unexplored;
|
||||
|
||||
if (_currentlyVisible[x, y])
|
||||
return FogOfWarState.Visible;
|
||||
|
||||
if (_explored[x, y])
|
||||
return FogOfWarState.Explored;
|
||||
|
||||
return FogOfWarState.Unexplored;
|
||||
}
|
||||
|
||||
private bool IsValidCoord(int x, int y)
|
||||
{
|
||||
return x >= 0 && x < _explored.GetLength(0) &&
|
||||
y >= 0 && y < _explored.GetLength(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Visible 데이터를 바이트 배열로 압축
|
||||
/// </summary>
|
||||
public byte[] GetVisibleData()
|
||||
{
|
||||
int totalCells = _width * _height;
|
||||
int byteCount = (totalCells + 7) / 8; // 8비트 = 1바이트
|
||||
byte[] data = new byte[byteCount];
|
||||
|
||||
for (int i = 0; i < totalCells; i++)
|
||||
{
|
||||
int x = i % _width;
|
||||
int y = i / _width;
|
||||
|
||||
if (_currentlyVisible[x, y])
|
||||
{
|
||||
int byteIndex = i / 8;
|
||||
int bitIndex = i % 8;
|
||||
data[byteIndex] |= (byte)(1 << bitIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explored 데이터를 바이트 배열로 압축
|
||||
/// </summary>
|
||||
public byte[] GetExploredData()
|
||||
{
|
||||
int totalCells = _width * _height;
|
||||
int byteCount = (totalCells + 7) / 8;
|
||||
byte[] data = new byte[byteCount];
|
||||
|
||||
for (int i = 0; i < totalCells; i++)
|
||||
{
|
||||
int x = i % _width;
|
||||
int y = i / _width;
|
||||
|
||||
if (_explored[x, y])
|
||||
{
|
||||
int byteIndex = i / 8;
|
||||
int bitIndex = i % 8;
|
||||
data[byteIndex] |= (byte)(1 << bitIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 바이트 배열에서 Visible 데이터 복원
|
||||
/// </summary>
|
||||
public void SetVisibleData(byte[] data)
|
||||
{
|
||||
System.Array.Clear(_currentlyVisible, 0, _currentlyVisible.Length);
|
||||
|
||||
int totalCells = _width * _height;
|
||||
for (int i = 0; i < totalCells && i / 8 < data.Length; i++)
|
||||
{
|
||||
int byteIndex = i / 8;
|
||||
int bitIndex = i % 8;
|
||||
|
||||
bool isVisible = (data[byteIndex] & (1 << bitIndex)) != 0;
|
||||
|
||||
int x = i % _width;
|
||||
int y = i / _width;
|
||||
_currentlyVisible[x, y] = isVisible;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 바이트 배열에서 Explored 데이터 복원
|
||||
/// </summary>
|
||||
public void SetExploredData(byte[] data)
|
||||
{
|
||||
int totalCells = _width * _height;
|
||||
for (int i = 0; i < totalCells && i / 8 < data.Length; i++)
|
||||
{
|
||||
int byteIndex = i / 8;
|
||||
int bitIndex = i % 8;
|
||||
|
||||
bool isExplored = (data[byteIndex] & (1 << bitIndex)) != 0;
|
||||
|
||||
int x = i % _width;
|
||||
int y = i / _width;
|
||||
|
||||
// Explored는 누적 (한번 탐색하면 계속 유지)
|
||||
if (isExplored)
|
||||
_explored[x, y] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 안개 상태
|
||||
/// </summary>
|
||||
public enum FogOfWarState
|
||||
{
|
||||
Unexplored, // 방문한 적 없음 (완전히 어두움)
|
||||
Explored, // 방문했지만 현재 시야 밖 (지형/건물만 보임)
|
||||
Visible // 현재 시야 안 (모두 보임)
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/FogOfWarSystem.cs.meta
Normal file
2
Assets/Scripts/FogOfWarSystem.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e73da29c7794e445a17b7d28a61cdb3
|
||||
30
Assets/Scripts/IVisionProvider.cs
Normal file
30
Assets/Scripts/IVisionProvider.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// 시야를 제공하는 객체 인터페이스 (플레이어, 건물 등)
|
||||
/// </summary>
|
||||
public interface IVisionProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// 소유자 클라이언트 ID
|
||||
/// </summary>
|
||||
ulong GetOwnerId();
|
||||
|
||||
/// <summary>
|
||||
/// 시야 범위
|
||||
/// </summary>
|
||||
float GetVisionRange();
|
||||
|
||||
/// <summary>
|
||||
/// Transform
|
||||
/// </summary>
|
||||
Transform GetTransform();
|
||||
|
||||
/// <summary>
|
||||
/// 현재 활성화 상태인지
|
||||
/// </summary>
|
||||
bool IsActive();
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/IVisionProvider.cs.meta
Normal file
2
Assets/Scripts/IVisionProvider.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b23d5408ff532f49a541af63882a724
|
||||
41
Assets/Scripts/PlayerVisionProvider.cs
Normal file
41
Assets/Scripts/PlayerVisionProvider.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// 플레이어의 시야 제공 컴포넌트
|
||||
/// </summary>
|
||||
public class PlayerVisionProvider : NetworkBehaviour, IVisionProvider
|
||||
{
|
||||
[Header("Vision Settings")]
|
||||
public float visionRange = 10f;
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
if (IsServer)
|
||||
{
|
||||
FogOfWarSystem.Instance?.RegisterVisionProvider(this);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
if (IsServer)
|
||||
{
|
||||
FogOfWarSystem.Instance?.UnregisterVisionProvider(this);
|
||||
}
|
||||
}
|
||||
|
||||
public ulong GetOwnerId() => OwnerClientId;
|
||||
public float GetVisionRange() => visionRange;
|
||||
public Transform GetTransform() => transform;
|
||||
public bool IsActive() => IsSpawned;
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
Gizmos.color = Color.yellow;
|
||||
Gizmos.DrawWireSphere(transform.position, visionRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/PlayerVisionProvider.cs.meta
Normal file
2
Assets/Scripts/PlayerVisionProvider.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 81d070574968f0d45be51307e2dfed64
|
||||
@@ -64,7 +64,7 @@ namespace Northbound
|
||||
int rechargeAmountToAdd = Mathf.Min(rechargeAmount, maxResources - _currentResources.Value);
|
||||
_currentResources.Value += rechargeAmountToAdd;
|
||||
|
||||
Debug.Log($"{resourceName} {rechargeAmountToAdd} 충전됨. 현재: {_currentResources.Value}/{maxResources}");
|
||||
// Debug.Log($"{resourceName} {rechargeAmountToAdd} 충전됨. 현재: {_currentResources.Value}/{maxResources}");
|
||||
}
|
||||
|
||||
_lastRechargeTime = Time.time;
|
||||
|
||||
Reference in New Issue
Block a user