네트워크 멀티플레이 대응
This commit is contained in:
@@ -75,22 +75,32 @@ namespace Northbound
|
||||
/// </summary>
|
||||
public void Initialize(BuildingData data, Vector3Int pos, int rot, ulong ownerId, TeamType team)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner)
|
||||
{
|
||||
InitializeServerRpc(data != null ? data.buildingName : "", pos.x, pos.y, pos.z, rot, ownerId, team);
|
||||
return;
|
||||
}
|
||||
|
||||
buildingData = data;
|
||||
gridPosition = pos;
|
||||
InitializeServerRpc(data != null ? data.buildingName : "", pos.x, pos.y, pos.z, rot, ownerId, team);
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
private void InitializeServerRpc(string buildingName, int posX, int posY, int posZ, int rot, ulong ownerId, TeamType team)
|
||||
{
|
||||
buildingData = BuildingManager.Instance?.availableBuildings.Find(b => b != null && b.buildingName == buildingName);
|
||||
gridPosition = new Vector3Int(posX, posY, posZ);
|
||||
rotation = rot;
|
||||
_ownerId.Value = ownerId;
|
||||
_team.Value = team;
|
||||
_currentProgress.Value = 0f;
|
||||
|
||||
// BuildingData의 크기를 기반으로 스케일 설정
|
||||
Vector3 size = data.GetSize(rot);
|
||||
|
||||
Vector3 size = buildingData != null ? buildingData.GetSize(rot) : Vector3.one;
|
||||
|
||||
// foundationVisual의 스케일만 조정 (토대 자체의 pivot은 중앙에 유지)
|
||||
if (foundationVisual != null)
|
||||
{
|
||||
// 토대 비주얼을 건물 크기에 맞게 조정 (높이는 얇게)
|
||||
// 토대 높이를 건물 크기에 맞게 조정 (높이는 얇게)
|
||||
foundationVisual.transform.localScale = new Vector3(size.x, 0.2f, size.z);
|
||||
foundationVisual.transform.localPosition = new Vector3(0, 0.1f, 0); // 바닥에서 약간 위
|
||||
}
|
||||
@@ -102,12 +112,31 @@ namespace Northbound
|
||||
_collider = gameObject.AddComponent<BoxCollider>();
|
||||
}
|
||||
|
||||
// 상호작용 가능한 크기로 설정 (전체 건물 높이가 아닌 접근 가능한 크기)
|
||||
// 상호작용 가능한 크기로 설정 (전체 건물 높이가 아니라 접근 가능한 크기)
|
||||
_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>");
|
||||
Debug.Log($"<color=yellow>[BuildingFoundation] 토대 생성: {buildingData?.buildingName ?? "Building"}, 크기: {size}, 위치: {transform.position}, Collider: {_collider.size}, 소유자: {ownerId}, 팀: {TeamManager.GetTeamName(team)}</color>");
|
||||
}
|
||||
|
||||
private void UpdateProgressBar()
|
||||
{
|
||||
if (buildingData == null || _progressBarInstance == null)
|
||||
return;
|
||||
|
||||
float progress = buildingData.requiredWorkAmount > 0 ? _currentProgress.Value / buildingData.requiredWorkAmount : 1f;
|
||||
|
||||
// 간단한 progress bar update - 필요한 경우 BuildingProgressBar 컴포넌트 사용
|
||||
var progressBarTransform = _progressBarInstance.transform;
|
||||
progressBarTransform.localScale = new Vector3(progress, 1f, 1f);
|
||||
}
|
||||
|
||||
private void OnProgressValueChanged(float previousValue, float newValue)
|
||||
{
|
||||
UpdateProgressBar();
|
||||
float max = buildingData != null ? buildingData.requiredWorkAmount : 100f;
|
||||
OnProgressChanged?.Invoke(newValue, max);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -123,6 +152,11 @@ namespace Northbound
|
||||
return new Bounds(transform.position + Vector3.up * size.y * 0.5f, size);
|
||||
}
|
||||
|
||||
public Bounds GetBounds()
|
||||
{
|
||||
return GetGridBounds();
|
||||
}
|
||||
|
||||
#region IInteractable Implementation
|
||||
|
||||
public bool CanInteract(ulong playerId)
|
||||
@@ -139,7 +173,7 @@ namespace Northbound
|
||||
Debug.Log($"[BuildingFoundation] Already completed");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 같은 팀만 건설 가능 - 플레이어의 팀을 가져와서 비교
|
||||
TeamType playerTeam = GetPlayerTeam(playerId);
|
||||
if (playerTeam != _team.Value)
|
||||
@@ -153,7 +187,8 @@ namespace Northbound
|
||||
|
||||
public void Interact(ulong playerId)
|
||||
{
|
||||
if (!IsServer || buildingData == null) return;
|
||||
if (!IsServer || buildingData == null)
|
||||
return;
|
||||
|
||||
if (!CanInteract(playerId))
|
||||
return;
|
||||
@@ -163,45 +198,71 @@ namespace Northbound
|
||||
// 건설 진행
|
||||
_currentProgress.Value += buildingData.workPerInteraction;
|
||||
|
||||
Debug.Log($"<color=green>[BuildingFoundation] 건설 진행: {_currentProgress.Value}/{buildingData.requiredWorkAmount} ({(_currentProgress.Value / buildingData.requiredWorkAmount * 100f):F1}%)</color>");
|
||||
Debug.Log($"<color=yellow>[BuildingFoundation] {buildingData.buildingName} 건설 진행: {_currentProgress.Value}/{buildingData.requiredWorkAmount}</color>");
|
||||
|
||||
// 완성 체크
|
||||
// 완료 체크
|
||||
if (_currentProgress.Value >= buildingData.requiredWorkAmount)
|
||||
{
|
||||
CompleteConstruction();
|
||||
CompleteConstruction(playerId);
|
||||
}
|
||||
}
|
||||
|
||||
private void CompleteConstruction(ulong playerId)
|
||||
{
|
||||
Debug.Log($"<color=green>[BuildingFoundation] {buildingData.buildingName} 건설 완료! 완성자: {playerId}</color>");
|
||||
|
||||
OnConstructionComplete?.Invoke();
|
||||
|
||||
// 토대 디스폰
|
||||
if (IsServer && NetworkObject != null)
|
||||
{
|
||||
NetworkObject.Despawn(true);
|
||||
}
|
||||
|
||||
// 상호작용 UI 제거
|
||||
if (_progressBarInstance != null)
|
||||
{
|
||||
Destroy(_progressBarInstance);
|
||||
_progressBarInstance = null;
|
||||
}
|
||||
}
|
||||
|
||||
private TeamType GetPlayerTeam(ulong playerId)
|
||||
{
|
||||
if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(playerId, out NetworkObject playerObj))
|
||||
{
|
||||
var teamMember = playerObj.GetComponent<ITeamMember>();
|
||||
if (teamMember != null)
|
||||
{
|
||||
return teamMember.GetTeam();
|
||||
}
|
||||
}
|
||||
return TeamType.Neutral;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IInteractable Implementation - Getters
|
||||
|
||||
public string GetInteractionPrompt()
|
||||
{
|
||||
if (buildingData == null)
|
||||
return "[E] 건설하기";
|
||||
|
||||
float percentage = (_currentProgress.Value / buildingData.requiredWorkAmount) * 100f;
|
||||
return $"[E] {buildingData.buildingName} 건설 ({percentage:F0}%)";
|
||||
return "건설하기";
|
||||
float workNeeded = buildingData.requiredWorkAmount - _currentProgress.Value;
|
||||
float interactionsNeeded = Mathf.Ceil(workNeeded / buildingData.workPerInteraction);
|
||||
return $"[{interactionsNeeded}] 건설하기";
|
||||
}
|
||||
|
||||
public string GetInteractionAnimation()
|
||||
{
|
||||
// BuildingData에서 애니메이션 트리거 가져오기
|
||||
if (buildingData != null && !string.IsNullOrEmpty(buildingData.constructionAnimationTrigger))
|
||||
{
|
||||
if (buildingData != null && buildingData.constructionAnimationTrigger != null)
|
||||
return buildingData.constructionAnimationTrigger;
|
||||
}
|
||||
|
||||
// 기본값: 빈 문자열 (애니메이션 없음)
|
||||
return "";
|
||||
return null;
|
||||
}
|
||||
|
||||
public EquipmentData GetEquipmentData()
|
||||
{
|
||||
// BuildingData에 건설 도구가 정의되어 있으면 반환
|
||||
if (buildingData != null && buildingData.constructionEquipment != null)
|
||||
{
|
||||
return buildingData.constructionEquipment;
|
||||
}
|
||||
|
||||
return null; // 특별한 도구 불필요
|
||||
return buildingData != null ? buildingData.constructionEquipment : null;
|
||||
}
|
||||
|
||||
public Transform GetTransform()
|
||||
@@ -213,136 +274,25 @@ namespace Northbound
|
||||
|
||||
#region ITeamMember Implementation
|
||||
|
||||
public TeamType GetTeam()
|
||||
{
|
||||
return _team.Value;
|
||||
}
|
||||
public TeamType GetTeam() => _team.Value;
|
||||
|
||||
public void SetTeam(TeamType team)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
if (!IsOwner)
|
||||
{
|
||||
SetTeamServerRpc(team);
|
||||
return;
|
||||
}
|
||||
|
||||
SetTeamServerRpc(team);
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
private void SetTeamServerRpc(TeamType team)
|
||||
{
|
||||
_team.Value = team;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 플레이어의 팀 가져오기
|
||||
/// </summary>
|
||||
private TeamType GetPlayerTeam(ulong playerId)
|
||||
{
|
||||
// 플레이어의 NetworkObject 찾기
|
||||
if (NetworkManager.Singleton != null && NetworkManager.Singleton.SpawnManager != null)
|
||||
{
|
||||
if (NetworkManager.Singleton.ConnectedClients.TryGetValue(playerId, out var client))
|
||||
{
|
||||
if (client.PlayerObject != null)
|
||||
{
|
||||
var teamMember = client.PlayerObject.GetComponent<ITeamMember>();
|
||||
if (teamMember != null)
|
||||
{
|
||||
return teamMember.GetTeam();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 기본값: 플레이어 팀
|
||||
return TeamType.Player;
|
||||
}
|
||||
|
||||
private void CompleteConstruction()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
|
||||
Debug.Log($"<color=cyan>[BuildingFoundation] 건물 완성! {buildingData.buildingName}</color>");
|
||||
|
||||
OnConstructionComplete?.Invoke();
|
||||
|
||||
// BuildingManager에서 토대 제거
|
||||
var buildingManager = BuildingManager.Instance;
|
||||
if (buildingManager != null)
|
||||
{
|
||||
buildingManager.RemoveFoundation(this);
|
||||
}
|
||||
|
||||
// 완성된 건물 생성
|
||||
SpawnCompletedBuilding();
|
||||
|
||||
// 토대 제거
|
||||
if (NetworkObject != null)
|
||||
{
|
||||
NetworkObject.Despawn(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnCompletedBuilding()
|
||||
{
|
||||
if (!IsServer || buildingData == null || buildingData.prefab == null)
|
||||
return;
|
||||
|
||||
// BuildingManager를 통해 건물 생성
|
||||
var buildingManager = BuildingManager.Instance;
|
||||
if (buildingManager != null)
|
||||
{
|
||||
buildingManager.SpawnCompletedBuildingServerRpc(
|
||||
buildingData.name,
|
||||
gridPosition,
|
||||
rotation,
|
||||
_ownerId.Value,
|
||||
_team.Value
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("[BuildingFoundation] BuildingManager를 찾을 수 없습니다!");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnProgressValueChanged(float oldValue, float newValue)
|
||||
{
|
||||
if (buildingData != null)
|
||||
{
|
||||
OnProgressChanged?.Invoke(newValue, buildingData.requiredWorkAmount);
|
||||
}
|
||||
UpdateProgressBar();
|
||||
}
|
||||
|
||||
private void UpdateProgressBar()
|
||||
{
|
||||
if (_progressBarInstance == null || buildingData == null) return;
|
||||
|
||||
// 진행바 UI 업데이트 (BuildingHealthBar와 유사한 구조 사용 가능)
|
||||
var progressBar = _progressBarInstance.GetComponent<BuildingHealthBar>();
|
||||
if (progressBar != null)
|
||||
{
|
||||
// BuildingHealthBar를 재사용하여 진행도 표시
|
||||
progressBar.UpdateHealth((int)_currentProgress.Value, (int)buildingData.requiredWorkAmount);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
if (buildingData == null) return;
|
||||
|
||||
// 건물 경계 표시 (노란색)
|
||||
Gizmos.color = Color.yellow;
|
||||
Vector3 size = buildingData.GetSize(rotation);
|
||||
Gizmos.DrawWireCube(transform.position + Vector3.up * size.y * 0.5f, size);
|
||||
|
||||
// Collider 경계 표시 (초록색)
|
||||
if (_collider != null)
|
||||
{
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawWireCube(transform.position + _collider.center, _collider.size);
|
||||
}
|
||||
|
||||
// 상호작용 가능 여부 표시
|
||||
if (_currentProgress.Value < (buildingData?.requiredWorkAmount ?? 100f))
|
||||
{
|
||||
Gizmos.color = Color.cyan;
|
||||
Gizmos.DrawSphere(transform.position + Vector3.up, 0.3f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user