건축모드 네트워크 환경 적용
This commit is contained in:
@@ -27,6 +27,13 @@ namespace Northbound
|
||||
public GameObject foundationVisual;
|
||||
public GameObject progressBarPrefab;
|
||||
|
||||
// 건물 데이터 인덱스 (네트워크 동기화용)
|
||||
private NetworkVariable<int> _buildingDataIndex = new NetworkVariable<int>(
|
||||
-1,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
// 현재 건설 진행도
|
||||
private NetworkVariable<float> _currentProgress = new NetworkVariable<float>(
|
||||
0f,
|
||||
@@ -63,6 +70,12 @@ namespace Northbound
|
||||
base.OnNetworkSpawn();
|
||||
|
||||
_currentProgress.OnValueChanged += OnProgressValueChanged;
|
||||
_buildingDataIndex.OnValueChanged += OnBuildingDataIndexChanged;
|
||||
|
||||
// 초기값 로드 시도 (Host/Client 모두 동일하게 처리)
|
||||
// NetworkVariable 초기값은 스폰 시 동기화되지만,
|
||||
// OnValueChanged는 변경 시만 발생하므로 초기값은 직접 로드해야 함
|
||||
LoadBuildingDataFromIndex(_buildingDataIndex.Value);
|
||||
|
||||
// 진행 UI 생성
|
||||
if (progressBarPrefab != null)
|
||||
@@ -75,10 +88,90 @@ namespace Northbound
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
_currentProgress.OnValueChanged -= OnProgressValueChanged;
|
||||
_buildingDataIndex.OnValueChanged -= OnBuildingDataIndexChanged;
|
||||
|
||||
base.OnNetworkDespawn();
|
||||
}
|
||||
|
||||
private void OnBuildingDataIndexChanged(int oldValue, int newValue)
|
||||
{
|
||||
LoadBuildingDataFromIndex(newValue);
|
||||
UpdateCollider();
|
||||
}
|
||||
|
||||
private void LoadBuildingDataFromIndex(int index)
|
||||
{
|
||||
var buildingManager = BuildingManager.Instance;
|
||||
if (buildingManager == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (index >= buildingManager.availableBuildings.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 이미 로드된 데이터와 동일하면 건너뜀
|
||||
TowerData newData = buildingManager.availableBuildings[index];
|
||||
if (buildingData == newData)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
buildingData = newData;
|
||||
|
||||
// buildingData 로드 후 업데이트
|
||||
UpdateCollider();
|
||||
UpdateVisual();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// BoxCollider 업데이트 (buildingData 기반)
|
||||
/// </summary>
|
||||
private void UpdateCollider()
|
||||
{
|
||||
if (buildingData == null)
|
||||
return;
|
||||
|
||||
Vector3 size = buildingData.GetSize(rotation);
|
||||
|
||||
// BoxCollider가 없으면 추가
|
||||
if (_collider == null)
|
||||
{
|
||||
_collider = GetComponent<BoxCollider>();
|
||||
if (_collider == null)
|
||||
{
|
||||
_collider = gameObject.AddComponent<BoxCollider>();
|
||||
}
|
||||
}
|
||||
|
||||
// 상호작용 가능한 크기로 설정 (전체 건물 높이가 아닌 접근 가능한 크기)
|
||||
_collider.size = new Vector3(size.x, 2f, size.z);
|
||||
_collider.center = new Vector3(0, 1f, 0);
|
||||
_collider.isTrigger = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Visual 스케일 업데이트 (buildingData 기반)
|
||||
/// </summary>
|
||||
private void UpdateVisual()
|
||||
{
|
||||
if (buildingData == null || foundationVisual == null)
|
||||
return;
|
||||
|
||||
Vector3 size = buildingData.GetSize(rotation);
|
||||
|
||||
// 토대 비주얼을 건물 크기에 맞게 조정 (높이는 얇게)
|
||||
foundationVisual.transform.localScale = new Vector3(size.x, 0.2f, size.z);
|
||||
foundationVisual.transform.localPosition = new Vector3(0, 0.1f, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 토대 초기화
|
||||
/// </summary>
|
||||
@@ -86,7 +179,31 @@ namespace Northbound
|
||||
{
|
||||
if (!IsServer) return;
|
||||
|
||||
// buildingData null 체크
|
||||
if (data == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// buildingData 인덱스 찾기
|
||||
var buildingManager = BuildingManager.Instance;
|
||||
if (buildingManager == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int dataIndex = buildingManager.availableBuildings.IndexOf(data);
|
||||
if (dataIndex < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 인덱스 설정 (네트워크 동기화됨)
|
||||
_buildingDataIndex.Value = dataIndex;
|
||||
|
||||
// 서버에서도 직접 데이터 로드
|
||||
buildingData = data;
|
||||
|
||||
gridPosition = pos;
|
||||
rotation = rot;
|
||||
_ownerId.Value = ownerId;
|
||||
@@ -95,7 +212,7 @@ namespace Northbound
|
||||
|
||||
// TowerData의 크기를 기반으로 스케일 설정
|
||||
Vector3 size = data.GetSize(rot);
|
||||
|
||||
|
||||
// foundationVisual의 스케일만 조정 (토대 자체의 pivot은 중앙에 유지)
|
||||
if (foundationVisual != null)
|
||||
{
|
||||
@@ -115,8 +232,6 @@ namespace Northbound
|
||||
_collider.size = new Vector3(size.x, 2f, size.z); // 높이를 2m로 설정하여 상호작용 가능
|
||||
_collider.center = new Vector3(0, 1f, 0); // 중심을 1m 높이에 배치
|
||||
_collider.isTrigger = false; // Trigger가 아닌 일반 Collider로 설정
|
||||
|
||||
Debug.Log($"<color=yellow>[BuildingFoundation] 토대 생성: {data.buildingName}, 크기: {size}, 위치: {transform.position}, Collider: {_collider.size}, 소유자: {ownerId}, 팀: {team}</color>");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -136,10 +251,15 @@ namespace Northbound
|
||||
|
||||
public bool CanInteract(ulong playerId)
|
||||
{
|
||||
// buildingData가 없으면 상호작용 불가
|
||||
if (buildingData == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 이미 완성됨
|
||||
if (_currentProgress.Value >= buildingData.requiredWorkAmount)
|
||||
{
|
||||
Debug.Log($"[BuildingFoundation] Already completed");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -153,7 +273,6 @@ namespace Northbound
|
||||
TeamType playerTeam = GetPlayerTeam(playerId);
|
||||
if (playerTeam != _team.Value)
|
||||
{
|
||||
Debug.LogWarning($"[BuildingFoundation] Wrong team: player={playerTeam}, foundation={_team.Value}");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -162,11 +281,28 @@ namespace Northbound
|
||||
|
||||
public void Interact(ulong playerId)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
// 네트워크 게임에서는 ServerRpc을 통해서만 서버에서 실행하도록 함
|
||||
RequestInteractServerRpc(playerId);
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
private void RequestInteractServerRpc(ulong playerId)
|
||||
{
|
||||
// 서버에서만 건설 진행 가능
|
||||
if (!IsServer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CanInteract(playerId))
|
||||
return;
|
||||
|
||||
// buildingData null 체크
|
||||
if (buildingData == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_lastInteractionTime = Time.time;
|
||||
|
||||
// 플레이어의 작업량 가져오기
|
||||
@@ -175,8 +311,6 @@ namespace Northbound
|
||||
// 건설 진행
|
||||
_currentProgress.Value += playerWorkPower;
|
||||
|
||||
Debug.Log($"<color=green>[BuildingFoundation] 건설 진행: {_currentProgress.Value}/{buildingData.requiredWorkAmount} ({(_currentProgress.Value / buildingData.requiredWorkAmount * 100f):F1}%) - 작업량: {playerWorkPower}</color>");
|
||||
|
||||
// 완성 체크
|
||||
if (_currentProgress.Value >= buildingData.requiredWorkAmount)
|
||||
{
|
||||
@@ -187,7 +321,8 @@ namespace Northbound
|
||||
public string GetInteractionPrompt()
|
||||
{
|
||||
string buildingName = buildingData != null ? buildingData.buildingName : "건물";
|
||||
float percentage = (_currentProgress.Value / buildingData.requiredWorkAmount) * 100f;
|
||||
float requiredWork = buildingData?.requiredWorkAmount ?? 100f;
|
||||
float percentage = (_currentProgress.Value / requiredWork) * 100f;
|
||||
return $"[E] {buildingName} 건설 ({percentage:F0}%)";
|
||||
}
|
||||
|
||||
@@ -270,7 +405,6 @@ namespace Northbound
|
||||
}
|
||||
|
||||
// 기본값: 10
|
||||
Debug.LogWarning($"[BuildingFoundation] 플레이어 {playerId}의 workPower를 찾을 수 없어 기본값 10을 사용합니다.");
|
||||
return 10f;
|
||||
}
|
||||
|
||||
@@ -278,7 +412,10 @@ namespace Northbound
|
||||
{
|
||||
if (!IsServer) return;
|
||||
|
||||
Debug.Log($"<color=cyan>[BuildingFoundation] 건물 완성! {buildingData.buildingName}</color>");
|
||||
if (buildingData == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnConstructionComplete?.Invoke();
|
||||
|
||||
@@ -324,7 +461,8 @@ namespace Northbound
|
||||
|
||||
private void OnProgressValueChanged(float oldValue, float newValue)
|
||||
{
|
||||
OnProgressChanged?.Invoke(newValue, buildingData.requiredWorkAmount);
|
||||
float requiredWork = buildingData?.requiredWorkAmount ?? 100f;
|
||||
OnProgressChanged?.Invoke(newValue, requiredWork);
|
||||
UpdateProgressBar();
|
||||
}
|
||||
|
||||
@@ -337,7 +475,8 @@ namespace Northbound
|
||||
if (progressBar != null)
|
||||
{
|
||||
// BuildingHealthBar를 재사용하여 진행도 표시
|
||||
progressBar.UpdateHealth((int)_currentProgress.Value, (int)buildingData.requiredWorkAmount);
|
||||
float requiredWork = buildingData?.requiredWorkAmount ?? 100f;
|
||||
progressBar.UpdateHealth((int)_currentProgress.Value, (int)requiredWork);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -400,34 +400,35 @@ namespace Northbound
|
||||
GameObject foundationObj = Instantiate(foundationPrefab, snappedPosition + data.placementOffset, Quaternion.Euler(0, rotation * 90f, 0));
|
||||
NetworkObject netObj = foundationObj.GetComponent<NetworkObject>();
|
||||
|
||||
// Add FogOfWarVisibility component to hide foundations in unexplored areas
|
||||
if (foundationObj.GetComponent<FogOfWarVisibility>() == null)
|
||||
{
|
||||
var visibility = foundationObj.AddComponent<FogOfWarVisibility>();
|
||||
visibility.showInExploredAreas = true; // Foundations remain visible in explored areas
|
||||
visibility.updateInterval = 0.2f;
|
||||
}
|
||||
|
||||
if (netObj != null)
|
||||
{
|
||||
netObj.SpawnWithOwnership(requestingClientId);
|
||||
// 스폰 먼저 실행 (서버 소유권으로 스폰)
|
||||
// 소유자 정보는 _ownerId NetworkVariable에 별도로 저장하므로 NetworkObject 소유권은 서버 유지
|
||||
netObj.Spawn();
|
||||
|
||||
// 스폰 후에 초기화 (OnNetworkSpawn 이후에 호출되어 타이밍 문제 해결)
|
||||
BuildingFoundation foundation = foundationObj.GetComponent<BuildingFoundation>();
|
||||
if (foundation != null)
|
||||
if (foundation == null)
|
||||
{
|
||||
foundation.Initialize(data, gridPosition, rotation, requestingClientId, playerTeam);
|
||||
placedFoundations.Add(foundation); // 토대 목록에 추가
|
||||
Debug.Log($"<color=yellow>[BuildingManager] {data.buildingName} 토대 생성 (소유자: {requestingClientId}, 위치: {gridPosition})</color>");
|
||||
Destroy(foundationObj);
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
// Initialize에서 NetworkVariable을 설정하고 데이터를 로드함
|
||||
foundation.Initialize(data, gridPosition, rotation, requestingClientId, playerTeam);
|
||||
|
||||
// Add FogOfWarVisibility component to hide foundations in unexplored areas
|
||||
if (foundationObj.GetComponent<FogOfWarVisibility>() == null)
|
||||
{
|
||||
Debug.LogError("<color=red>[BuildingManager] BuildingFoundation 컴포넌트가 없습니다!</color>");
|
||||
netObj.Despawn(true);
|
||||
var visibility = foundationObj.AddComponent<FogOfWarVisibility>();
|
||||
visibility.showInExploredAreas = true; // Foundations remain visible in explored areas
|
||||
visibility.updateInterval = 0.2f;
|
||||
}
|
||||
|
||||
placedFoundations.Add(foundation); // 토대 목록에 추가
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("<color=red>[BuildingManager] NetworkObject 컴포넌트가 없습니다!</color>");
|
||||
Destroy(foundationObj);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user