건축모드 네트워크 환경 적용

This commit is contained in:
2026-02-14 17:45:31 +09:00
parent 3e1754eb3c
commit e451d95e0e
4 changed files with 195 additions and 32 deletions

View File

@@ -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);
}
}