Compare commits

...

2 Commits

Author SHA1 Message Date
0a2119a548 팀원들 모두가 시야를 공유하도록 함 2026-02-25 21:45:06 +09:00
23429ec096 클라이언트의 이동속도가 2배인 문제 수정
로컬 컨트롤러를 제거하고 네트워크 컨트롤러만 남겨둠
2026-02-25 21:40:50 +09:00
7 changed files with 107 additions and 83 deletions

View File

@@ -67,7 +67,6 @@
<Compile Include="Assets\Scripts\FogOfWarRenderer.cs" />
<Compile Include="Assets\Scripts\ResourcePickup.cs" />
<Compile Include="Assets\Scripts\PlayerVisionProvider.cs" />
<Compile Include="Assets\Scripts\PlayerController.cs" />
<Compile Include="Assets\FlatKit\Demos\Common\Scripts\Motion\OrbitMotion.cs" />
<Compile Include="Assets\Scripts\CreepCamp.cs" />
<Compile Include="Assets\Scripts\EnemyAIController.cs" />

View File

@@ -342,12 +342,14 @@ namespace Northbound
fogData.ClearCurrentVision();
}
// 모든 시야 제공자의 시야 범위 계산
// 모든 시야 제공자의 시야 범위 계산 (팀 시야 공유)
foreach (var provider in _visionProviders)
{
if (provider == null || !provider.IsActive()) continue;
ulong ownerId = provider.GetOwnerId();
TeamType providerTeam = provider.GetTeam();
if (!_serverFogData.ContainsKey(ownerId))
{
_serverFogData[ownerId] = new FogOfWarData(gridWidth, gridHeight);
@@ -356,7 +358,8 @@ namespace Northbound
Vector3 position = provider.GetTransform().position;
float visionRange = provider.GetVisionRange();
RevealArea(ownerId, position, visionRange);
// 같은 팀의 모든 멤버에게 시야 공유
RevealAreaForTeam(providerTeam, position, visionRange);
}
// 각 클라이언트에게 시야 데이터 전송
@@ -387,6 +390,47 @@ namespace Northbound
}
}
/// <summary>
/// 같은 팀의 모든 멤버에게 시야 공개
/// </summary>
private void RevealAreaForTeam(TeamType team, Vector3 worldPosition, float radius)
{
// 해당 팀의 모든 멤버 찾기
foreach (var kvp in _serverFogData)
{
ulong clientId = kvp.Key;
// 클라이언트의 팀 확인
if (GetClientTeam(clientId) == team)
{
RevealArea(clientId, worldPosition, radius);
}
}
}
/// <summary>
/// 클라이언트의 팀 가져오기
/// </summary>
private TeamType GetClientTeam(ulong clientId)
{
if (NetworkManager.Singleton == null) return TeamType.Neutral;
// 연결된 클라이언트에서 플레이어 오브젝트 찾기
if (NetworkManager.Singleton.SpawnManager.SpawnedObjects != null)
{
foreach (var netObj in NetworkManager.Singleton.SpawnManager.SpawnedObjects.Values)
{
var playerController = netObj.GetComponent<NetworkPlayerController>();
if (playerController != null && playerController.OwnerPlayerId == clientId)
{
return playerController.GetTeam();
}
}
}
return TeamType.Neutral;
}
/// <summary>
/// 클라이언트에게 안개 데이터 전송
/// </summary>

View File

@@ -26,5 +26,10 @@ namespace Northbound
/// 현재 활성화 상태인지
/// </summary>
bool IsActive();
/// <summary>
/// 소속 팀 (팀 시야 공유용)
/// </summary>
TeamType GetTeam();
}
}

View File

@@ -165,6 +165,14 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl
base.OnNetworkDespawn();
}
// 이동 동기화 설정
private float _moveSendInterval = 0.016f; // ~60Hz 전송
private float _moveSendTimer;
private Vector2 _lastSentMoveInput;
// 서버 측 이동 입력 저장
private Vector2 _serverMoveInput;
void Update()
{
// 서버에서 체력 자연 회복 처리
@@ -185,47 +193,65 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl
// 액션/상호작용 중이면 이동 불가
var attackAction = GetComponent<AttackAction>();
var playerInteraction = GetComponent<PlayerInteraction>();
bool isActionBlocked = (attackAction != null && attackAction.IsAttacking) ||
(playerInteraction != null && playerInteraction.IsInteracting);
if (isActionBlocked)
if (isActionBlocked)
{
// 서버에 이동 중지 알림 (애니메이션 포함)
MoveServerRpc(Vector2.zero);
// 서버에 이동 중지 알림
if (_lastSentMoveInput != Vector2.zero)
{
_lastSentMoveInput = Vector2.zero;
MoveServerRpc(Vector2.zero);
}
return;
}
_moveInput = _inputActions.Player.Move.ReadValue<Vector2>();
// 서버에 이동 요청 전송 (애니메이션 포함)
MoveServerRpc(_moveInput);
// 일정 간격으로만 서버에 전송 (네트워크 부하 감소)
_moveSendTimer += Time.deltaTime;
if (_moveSendTimer >= _moveSendInterval || _moveInput != _lastSentMoveInput)
{
_moveSendTimer = 0f;
_lastSentMoveInput = _moveInput;
MoveServerRpc(_moveInput);
}
}
void FixedUpdate()
{
// 서버에서만 물리 이동 처리
if (!IsServer) return;
if (_currentHealth.Value <= 0) return;
Vector3 move = new Vector3(_serverMoveInput.x, 0, _serverMoveInput.y).normalized;
if (move.magnitude >= 0.1f)
{
Quaternion targetRotation = Quaternion.LookRotation(move);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.fixedDeltaTime);
if (_controller != null)
{
float moveSpeed = _playerStats?.GetMoveSpeed() ?? 5f;
_controller.Move(move * moveSpeed * Time.fixedDeltaTime);
}
}
// 애니메이션 업데이트
if (_networkAnimator != null)
{
_networkAnimator.Animator.SetFloat("MoveSpeed", move.magnitude);
}
}
[Rpc(SendTo.Server)]
private void MoveServerRpc(Vector2 moveInput)
{
// 죽었으면 이동 불가
if (_currentHealth.Value <= 0) return;
Vector3 move = new Vector3(moveInput.x, 0, moveInput.y).normalized;
// NetworkAnimator로 애니메이션 동기화
if (_networkAnimator != null)
{
_networkAnimator.Animator.SetFloat("MoveSpeed", move.magnitude);
}
if (move.magnitude >= 0.1f)
{
Quaternion targetRotation = Quaternion.LookRotation(move);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
if (_controller != null)
{
_controller.Move(move * (_playerStats?.GetMoveSpeed() ?? 5f) * Time.deltaTime);
}
}
// 서버에 입력만 저장, 실제 이동은 FixedUpdate에서 처리
_serverMoveInput = moveInput;
}
#region ITeamMember Implementation

View File

@@ -1,40 +0,0 @@
using Northbound;
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
public class PlayerController : MonoBehaviour
{
[Header("Movement Settings")]
[SerializeField] private float moveSpeed = 5f;
private Rigidbody rb;
private Vector3 moveDirection;
private PlayerInteraction playerInteraction;
void Start()
{
rb = GetComponent<Rigidbody>();
rb.constraints = RigidbodyConstraints.FreezeRotation;
playerInteraction = GetComponent<PlayerInteraction>();
}
void Update()
{
if (playerInteraction != null && playerInteraction.IsInteracting)
return;
float horizontal = Input.GetAxisRaw("Horizontal");
float vertical = Input.GetAxisRaw("Vertical");
moveDirection = new Vector3(horizontal, 0f, vertical).normalized;
}
void FixedUpdate()
{
rb.linearVelocity = new Vector3(
moveDirection.x * moveSpeed,
rb.linearVelocity.y,
moveDirection.z * moveSpeed
);
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 7a3e5b8c4d2f1a9e6b0c3d7e8f1a2b3c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -35,6 +35,7 @@ namespace Northbound
public float GetVisionRange() => _playerStats?.GetSight() ?? 10f;
public Transform GetTransform() => transform;
public bool IsActive() => IsSpawned;
public TeamType GetTeam() => _playerController?.GetTeam() ?? TeamType.Player;
private void OnDrawGizmosSelected()
{