diff --git a/Assets/Data/Scripts/DataClasses/TowerDataExtensions.cs b/Assets/Data/Scripts/DataClasses/TowerDataExtensions.cs
index 9508822..e174e81 100644
--- a/Assets/Data/Scripts/DataClasses/TowerDataExtensions.cs
+++ b/Assets/Data/Scripts/DataClasses/TowerDataExtensions.cs
@@ -38,7 +38,7 @@ namespace Northbound.Data
public int length => sizeZ; // Z축 (깊이)
public float height => sizeY; // Y축 (높이)
public int maxHealth => maxHp;
- public float visionRange => atkRange;
+ public float visionRange => sight;
public float requiredWorkAmount => manpower;
public Vector3 GetSize(int rotation)
diff --git a/Assets/Prefabs/Core.asset b/Assets/Prefabs/Core.asset
new file mode 100644
index 0000000..dc40c81
--- /dev/null
+++ b/Assets/Prefabs/Core.asset
@@ -0,0 +1,47 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 8c40fef5ebc37b743a3f225c1ca57c32, type: 3}
+ m_Name: Core
+ m_EditorClassIdentifier: Assembly-CSharp::Northbound.Data.TowerData
+ id: 0
+ memo:
+ buildingName:
+ level: 0
+ upgradeTo: 0
+ towerType:
+ mana: 0
+ manpower: 0
+ sizeX: 0
+ sizeY: 0
+ sizeZ: 0
+ maxHp: 0
+ sight: 100
+ atkRange: 0
+ atkDamage: 0
+ atkIntervalSec: 0
+ modelPath:
+ prefab: {fileID: 0}
+ icon: {fileID: 0}
+ placementOffset: {x: 0, y: 0, z: 0}
+ allowRotation: 1
+ isIndestructible: 0
+ autoRegenerate: 0
+ regenPerSecond: 1
+ providesVision: 1
+ constructionEquipment:
+ socketName: RightHand
+ equipmentPrefab: {fileID: 0}
+ attachOnStart: 1
+ detachOnEnd: 1
+ keepEquipped: 0
+ attachDelay: 0
+ detachDelay: 0
diff --git a/Assets/Prefabs/Core.asset.meta b/Assets/Prefabs/Core.asset.meta
new file mode 100644
index 0000000..cab2ec9
--- /dev/null
+++ b/Assets/Prefabs/Core.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8e9cb7f0c2209b543b171709534789aa
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Prefabs/Core.prefab b/Assets/Prefabs/Core.prefab
index cfa7314..35716e9 100644
--- a/Assets/Prefabs/Core.prefab
+++ b/Assets/Prefabs/Core.prefab
@@ -49,7 +49,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
- GlobalObjectIdHash: 3998537868
+ GlobalObjectIdHash: 615747208
InScenePlacedSourceGlobalObjectIdHash: 615747208
DeferredDespawnTick: 0
Ownership: 0
@@ -106,7 +106,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::Northbound.Building
ShowTopMostFoldoutHeaderGroup: 1
- buildingData: {fileID: 0}
+ buildingData: {fileID: 11400000, guid: 8e9cb7f0c2209b543b171709534789aa, type: 2}
gridPosition: {x: 0, y: 0, z: 0}
rotation: 0
initialTeam: 1
@@ -152,10 +152,12 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 760137a2fd0da7f458ac4b0ee7f485d6, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::Northbound.FogOfWarVisibility
+ alwaysVisible: 1
showInExploredAreas: 1
updateInterval: 0.2
renderers: []
enableDistantVisibility: 1
+ baseVisibilityRange: 50
heightVisibilityMultiplier: 2
minHeightForDistantVisibility: 3
useExploredMaterial: 0
diff --git a/Assets/Scripts/Buildings/Blacksmith.cs b/Assets/Scripts/Buildings/Blacksmith.cs
index 333eaeb..1b08f04 100644
--- a/Assets/Scripts/Buildings/Blacksmith.cs
+++ b/Assets/Scripts/Buildings/Blacksmith.cs
@@ -6,8 +6,16 @@ namespace Northbound
///
/// 블랙스미스 건물 - 업그레이드 구매 가능
///
- public class Blacksmith : MonoBehaviour, IInteractable
+ public class Blacksmith : NetworkBehaviour, IInteractable, IVisionProvider, ITeamMember
{
+ [Header("Vision Settings")]
+ [Tooltip("블랙스미스가 제공하는 시야 범위")]
+ public float visionRange = 10f;
+
+ [Header("Team")]
+ [Tooltip("건물의 팀")]
+ public TeamType initialTeam = TeamType.Player;
+
[Header("UI Reference")]
[SerializeField] private GameObject _upgradePopupPrefab;
@@ -55,9 +63,6 @@ namespace Northbound
return transform;
}
-
-
-
#endregion
private void OpenUpgradePopup(ulong playerId)
@@ -115,12 +120,63 @@ namespace Northbound
return null;
}
- private void OnDestroy()
+ public override void OnDestroy()
{
if (_popupInstance != null)
{
Destroy(_popupInstance);
}
+ base.OnDestroy();
}
+
+ #region NetworkBehaviour Overrides
+
+ public override void OnNetworkSpawn()
+ {
+ if (IsServer)
+ {
+ // 시야 제공자로 등록
+ FogOfWarSystem.Instance?.RegisterVisionProvider(this);
+ }
+ }
+
+ public override void OnNetworkDespawn()
+ {
+ if (IsServer)
+ {
+ // 시야 제공자 등록 해제
+ FogOfWarSystem.Instance?.UnregisterVisionProvider(this);
+ }
+ }
+
+ #endregion
+
+ #region IVisionProvider Implementation
+
+ public ulong GetOwnerId() => 0; // 블랙스미스는 모든 플레이어에게 시야 제공
+
+ public float GetVisionRange() => visionRange;
+
+ Transform IVisionProvider.GetTransform() => transform;
+
+ public bool IsActive() => IsSpawned;
+
+ TeamType IVisionProvider.GetTeam() => initialTeam;
+
+ #endregion
+
+ #region ITeamMember Implementation
+
+ public TeamType GetTeam() => initialTeam;
+
+ public bool IsDead() => false; // 블랙스미스는 파괴되지 않음
+
+ public void SetTeam(TeamType team)
+ {
+ // 블랙스미스의 팀은 변경할 수 없음
+ Debug.LogWarning("[Blacksmith] 블랙스미스의 팀은 변경할 수 없습니다.");
+ }
+
+ #endregion
}
}
diff --git a/Assets/Scripts/Core.cs b/Assets/Scripts/Core.cs
index b21d0c4..5e4fbe6 100644
--- a/Assets/Scripts/Core.cs
+++ b/Assets/Scripts/Core.cs
@@ -7,12 +7,15 @@ namespace Northbound
///
/// 플레이어가 자원을 건내받아 게임의 전역 자원으로 관리하는 중앙 허브
///
- public class Core : NetworkBehaviour, IInteractable, IDamageable, ITeamMember
+ public class Core : NetworkBehaviour, IInteractable, IDamageable, ITeamMember, IVisionProvider
{
[Header("Core Settings")]
public int maxStorageCapacity = 1000; // 코어의 최대 저장 용량
public bool unlimitedStorage = false; // 무제한 저장소
+ [Header("Vision")]
+ public float visionRange = 15f; // 코어가 제공하는 시야 범위
+
[Header("Health")]
public int maxHealth = 1000;
public GameObject damageEffectPrefab;
@@ -55,6 +58,9 @@ namespace Northbound
{
_totalResources.Value = 0;
_currentHealth.Value = maxHealth;
+
+ // 시야 제공자로 등록
+ FogOfWarSystem.Instance?.RegisterVisionProvider(this);
}
_currentHealth.OnValueChanged += OnHealthChanged;
@@ -62,6 +68,12 @@ namespace Northbound
public override void OnNetworkDespawn()
{
+ if (IsServer)
+ {
+ // 시야 제공자 등록 해제
+ FogOfWarSystem.Instance?.UnregisterVisionProvider(this);
+ }
+
_currentHealth.OnValueChanged -= OnHealthChanged;
}
@@ -372,5 +384,19 @@ namespace Northbound
}
#endregion
+
+ #region IVisionProvider Implementation
+
+ public ulong GetOwnerId() => 0; // 코어는 모든 플레이어에게 시야 제공
+
+ public float GetVisionRange() => visionRange;
+
+ Transform IVisionProvider.GetTransform() => transform;
+
+ public bool IsActive() => IsSpawned && !IsDead();
+
+ TeamType IVisionProvider.GetTeam() => TeamType.Player;
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/Assets/Scripts/FogOfWarRenderer.cs b/Assets/Scripts/FogOfWarRenderer.cs
index f360997..9a62841 100644
--- a/Assets/Scripts/FogOfWarRenderer.cs
+++ b/Assets/Scripts/FogOfWarRenderer.cs
@@ -182,17 +182,39 @@ namespace Northbound
{
if (NetworkManager.Singleton == null) return ulong.MaxValue;
- var localPlayer = NetworkManager.Singleton.SpawnManager.GetLocalPlayerObject();
- if (localPlayer != null)
+ // 방법 1: SpawnManager에서 찾기
+ NetworkObject localPlayer = null;
+ if (NetworkManager.Singleton.SpawnManager != null)
{
- var playerController = localPlayer.GetComponent();
- if (playerController != null)
+ localPlayer = NetworkManager.Singleton.SpawnManager.GetLocalPlayerObject();
+ }
+
+ // 방법 2: LocalClient에서 찾기
+ if (localPlayer == null && NetworkManager.Singleton.LocalClient != null)
+ {
+ localPlayer = NetworkManager.Singleton.LocalClient.PlayerObject;
+ }
+
+ // 방법 3: 직접 검색 (IsLocalPlayer인 플레이어 찾기)
+ if (localPlayer == null)
+ {
+ var allPlayers = FindObjectsByType(FindObjectsSortMode.None);
+ foreach (var player in allPlayers)
{
- return playerController.OwnerPlayerId;
+ if (player.IsLocalPlayer)
+ {
+ localPlayer = player.GetComponent();
+ break;
+ }
}
}
- return ulong.MaxValue;
+ if (localPlayer == null) return ulong.MaxValue;
+
+ var playerController = localPlayer.GetComponent();
+ if (playerController == null) return ulong.MaxValue;
+
+ return playerController.OwnerPlayerId;
}
///
diff --git a/Assets/Scripts/FogOfWarSystem.cs b/Assets/Scripts/FogOfWarSystem.cs
index 9ecef51..92fc709 100644
--- a/Assets/Scripts/FogOfWarSystem.cs
+++ b/Assets/Scripts/FogOfWarSystem.cs
@@ -307,7 +307,8 @@ namespace Northbound
if (!_visionProviders.Contains(provider))
{
_visionProviders.Add(provider);
- // Debug.Log($"[FogOfWar] Vision Provider 등록: {provider.GetTransform().name} (Owner: {provider.GetOwnerId()})");
+ // 즉시 시야 업데이트 트리거
+ _updateTimer = updateInterval;
}
}
diff --git a/Assets/Scripts/FogOfWarVisibility.cs b/Assets/Scripts/FogOfWarVisibility.cs
index ac2ac73..20a3e8c 100644
--- a/Assets/Scripts/FogOfWarVisibility.cs
+++ b/Assets/Scripts/FogOfWarVisibility.cs
@@ -10,6 +10,9 @@ namespace Northbound
public class FogOfWarVisibility : MonoBehaviour
{
[Header("Visibility Settings")]
+ [Tooltip("Always show this object regardless of fog state (for core, important structures)")]
+ public bool alwaysVisible = false;
+
[Tooltip("Show this object in explored areas (greyed out) or only when visible")]
public bool showInExploredAreas = false;
@@ -19,10 +22,13 @@ namespace Northbound
[Tooltip("Renderers to show/hide (auto-detected if empty)")]
public Renderer[] renderers;
- [Header("Height-Based Distant Visibility")]
+ [Header("Extended Visibility")]
[Tooltip("Enable visibility from farther away based on object height")]
public bool enableDistantVisibility = true;
+ [Tooltip("Base visibility range (additional range beyond player vision)")]
+ public float baseVisibilityRange = 10f;
+
[Tooltip("Visibility range multiplier per unit of height (default: 2x vision per 1m height)")]
public float heightVisibilityMultiplier = 2.0f;
@@ -76,8 +82,17 @@ namespace Northbound
// CRITICAL: Start hidden and stay hidden until fog system confirms visibility
// 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);
+ // 단, alwaysVisible이면 항상 보임
+ if (alwaysVisible)
+ {
+ _isVisible = false;
+ SetVisible(true);
+ }
+ else
+ {
+ _isVisible = true; // Set to true first so SetVisible(false) actually runs
+ SetVisible(false);
+ }
}
///
@@ -165,21 +180,50 @@ namespace Northbound
{
if (NetworkManager.Singleton == null) return ulong.MaxValue;
- var localPlayer = NetworkManager.Singleton.SpawnManager.GetLocalPlayerObject();
- if (localPlayer != null)
+ // 방법 1: SpawnManager에서 찾기
+ NetworkObject localPlayer = null;
+ if (NetworkManager.Singleton.SpawnManager != null)
{
- var playerController = localPlayer.GetComponent();
- if (playerController != null)
+ localPlayer = NetworkManager.Singleton.SpawnManager.GetLocalPlayerObject();
+ }
+
+ // 방법 2: LocalClient에서 찾기
+ if (localPlayer == null && NetworkManager.Singleton.LocalClient != null)
+ {
+ localPlayer = NetworkManager.Singleton.LocalClient.PlayerObject;
+ }
+
+ // 방법 3: 직접 검색 (IsLocalPlayer인 플레이어 찾기)
+ if (localPlayer == null)
+ {
+ var allPlayers = FindObjectsByType(FindObjectsSortMode.None);
+ foreach (var player in allPlayers)
{
- return playerController.OwnerPlayerId;
+ if (player.IsLocalPlayer)
+ {
+ localPlayer = player.GetComponent();
+ break;
+ }
}
}
- return ulong.MaxValue;
+ if (localPlayer == null) return ulong.MaxValue;
+
+ var playerController = localPlayer.GetComponent();
+ if (playerController == null) return ulong.MaxValue;
+
+ return playerController.OwnerPlayerId;
}
private void UpdateVisibility()
{
+ // 항상 보이는 객체는 fog 체크 안함
+ if (alwaysVisible)
+ {
+ SetVisible(true);
+ return;
+ }
+
var fogSystem = FogOfWarSystem.Instance;
if (fogSystem == null)
{
@@ -268,11 +312,14 @@ namespace Northbound
// 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;
+ float extendedRange = 0f;
+ if (_objectHeight >= minHeightForDistantVisibility)
+ {
+ 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;
+ // Total range = player vision + base visibility + height bonus
+ float totalRange = baseVisibilityRange + extendedRange;
return distanceToPlayer <= totalRange;
}
diff --git a/Assets/Scripts/WorkerSpawner.cs b/Assets/Scripts/WorkerSpawner.cs
index c33e1c9..732f4b2 100644
--- a/Assets/Scripts/WorkerSpawner.cs
+++ b/Assets/Scripts/WorkerSpawner.cs
@@ -3,8 +3,16 @@ using UnityEngine;
namespace Northbound
{
- public class WorkerSpawner : NetworkBehaviour, IInteractable
+ public class WorkerSpawner : NetworkBehaviour, IInteractable, IVisionProvider, ITeamMember
{
+ [Header("Vision Settings")]
+ [Tooltip("워커 홀이 제공하는 시야 범위")]
+ public float visionRange = 10f;
+
+ [Header("Team")]
+ [Tooltip("건물의 팀")]
+ public TeamType initialTeam = TeamType.Player;
+
[Header("Spawner Settings")]
public GameObject workerPrefab;
public Transform spawnPoint;
@@ -34,11 +42,24 @@ namespace Northbound
{
base.OnNetworkSpawn();
_workerCount.OnValueChanged += OnWorkerCountChanged;
+
+ if (IsServer)
+ {
+ // 시야 제공자로 등록
+ FogOfWarSystem.Instance?.RegisterVisionProvider(this);
+ }
}
public override void OnNetworkDespawn()
{
_workerCount.OnValueChanged -= OnWorkerCountChanged;
+
+ if (IsServer)
+ {
+ // 시야 제공자 등록 해제
+ FogOfWarSystem.Instance?.UnregisterVisionProvider(this);
+ }
+
base.OnNetworkDespawn();
}
@@ -240,9 +261,37 @@ namespace Northbound
Vector3 spawnCenter = spawnPoint != null ? spawnPoint.position : transform.position;
Gizmos.DrawWireSphere(spawnCenter, spawnRadius);
- UnityEditor.Handles.Label(spawnCenter + Vector3.up * 2f,
+ UnityEditor.Handles.Label(spawnCenter + Vector3.up * 2f,
$"Worker Spawner\nWorkers: {_workerCount.Value}/{maxWorkers}");
#endif
}
+
+ #region IVisionProvider Implementation
+
+ public ulong GetOwnerId() => 0; // 워커 홀은 모든 플레이어에게 시야 제공
+
+ public float GetVisionRange() => visionRange;
+
+ Transform IVisionProvider.GetTransform() => transform;
+
+ public bool IsActive() => IsSpawned;
+
+ TeamType IVisionProvider.GetTeam() => initialTeam;
+
+ #endregion
+
+ #region ITeamMember Implementation
+
+ public TeamType GetTeam() => initialTeam;
+
+ public bool IsDead() => false; // 워커 홀은 파괴되지 않음
+
+ public void SetTeam(TeamType team)
+ {
+ // 워커 홀의 팀은 변경할 수 없음
+ Debug.LogWarning("[WorkerSpawner] 워커 홀의 팀은 변경할 수 없습니다.");
+ }
+
+ #endregion
}
}