프로젝트 팀장 분과 의논을 한 뒤, 마무리 수정을 몇가지 하게 되었다.
우선 NetworkSimulatedSmoothLocationTime, ListenServerNetworkSimulatedSmoothLocationTime 두 변수의 활용에 대해서이다.
두 변수의 역할은 다음과 같다.
NetworkSimulatedSmoothLocationTime: 클라이언트내 서버 pawn 위치를 보정하는 시간
ListenServerNetworkSimulatedSmoothLocationTime : 서버 내 클라이언트 pawn 위치를 보정하는 시간
이전 코드는 서버, 클라이언트 할 것 없이 각각의 머신에서 무조건 저 두 변수의 값을 변경하는 방식으로 했지만,
사실 클라이언트에서는 NetworkSimulatedSmoothLocationTime의 값만,
서버에서는 ListenServerNetworkSimulatedSmoothLocationTime의 값만 바꿔주면 된다.
이 점을 고려하여 함수를 새로 만들었다.
// must always be applied to the other person’s character.
void AISeeMeCharacter::SetSmoothCharacterMovement(bool bEnable)
{
if (GetNetMode() == NM_ListenServer) // server
{
if (bEnable)
{
GetCharacterMovement()->ListenServerNetworkSimulatedSmoothLocationTime = 0.3f;
}
else
{
GetCharacterMovement()->ListenServerNetworkSimulatedSmoothLocationTime = 0.04f;
}
GetCharacterMovement()->ResetPredictionData_Client(); // important
}
else // client
{
if (bEnable)
{
GetCharacterMovement()->NetworkSimulatedSmoothLocationTime = 0.4f;
}
else
{
GetCharacterMovement()->NetworkSimulatedSmoothLocationTime = 0.1f;
}
GetCharacterMovement()->ResetPredictionData_Client();
}
}
처음에 인게임에서 SmoothTime을 바꿔도 적용이 되지 않고 한번 나갔다 들어와야 적용이 되는 문제가 발생하였다.
결론부터 말하자면, 이에 대한 해결을 위해 중간에 GetCharacterMovement()->ResetPredictionData_Client(); 이 문장이 반드시 필요했다.
처음에 문제 해결을 위해 CharacterMovementComponent(CMC) 코드를 분석하여 NetworkSimulatedSmoothLocationTime의 참조를 찾아봤더니, 다음과 같이 쓰이고 있는 것을 볼 수 있었다.
FNetworkPredictionData_Client_Character::FNetworkPredictionData_Client_Character(const UCharacterMovementComponent& ClientMovement)
: ClientUpdateRealTime(0.f)
, CurrentTimeStamp(0.f)
//...
{
MaxSmoothNetUpdateDist = ClientMovement.NetworkMaxSmoothUpdateDistance;
NoSmoothNetUpdateDist = ClientMovement.NetworkNoSmoothUpdateDistance;
const bool bIsListenServer = (ClientMovement.GetNetMode() == NM_ListenServer);
/** 여기에서 사용됨 */
SmoothNetUpdateTime = (bIsListenServer ? ClientMovement.ListenServerNetworkSimulatedSmoothLocationTime
: ClientMovement.NetworkSimulatedSmoothLocationTime);
SmoothNetUpdateRotationTime = (bIsListenServer ? ClientMovement.ListenServerNetworkSimulatedSmoothRotationTime
: ClientMovement.NetworkSimulatedSmoothRotationTime);
const AGameNetworkManager* GameNetworkManager = (const AGameNetworkManager*)(AGameNetworkManager::StaticClass()->GetDefaultObject());
if (GameNetworkManager)
{
//...
}
이렇게 FNetworkPredictionData_Client_Character 클래스의 생성자 안에서 SmoothNetUpdateTime, SmoothNetUpdateRotationTime을 정하는데 단 한번 참조되고, 그 뒤에는 사용되지 않고 있었다.
게임이 실행되는 중에 NetworkSimulatedSmoothLocationTime, ListenServerNetworkSimulatedSmoothLocationTime을 아무리 바꿔봤자 이미 FNetworkPredictionData_Client_Character는 초기값을 토대로 만들어져서 사용되고 있었을 것이므로, 스무딩이 적용될 턱이 없었다.
해결을 위해서는 업데이트된 NetworkSimulatedSmoothLocationTime, ListenServerNetworkSimulatedSmoothLocationTime 변수를 FNetworkPredictionData_Client_Character 생성자의 인자로 하여 FNetworkPredictionData_Client_Character를 새로 다시 만들어줘야 했다.
이 방법을 찾기 위해 CMC 코드를 분석하여 찾은 함수가 ResetPredictionData_Client였다.
void UCharacterMovementComponent::ResetPredictionData_Client()
{
ForceClientAdjustment();
if (ClientPredictionData)
{
delete ClientPredictionData;
ClientPredictionData = nullptr;
}
}
ClientPredictionData를 싹 지워주며,
ClientPredictionData가 바로 FNetworkPredictionData_Client_Character 포인터 변수이다.
ClientPredictionData가 지워지면, CMC는 PredictionData를 다시 만들어내려 할 것이고, 이 때는 변경한 SmoothTime이 잘 반영될 터였다.
실제로 테스트 결과, 의도대로 잘 작동하는 것을 볼 수 있었다.
두번째 변경점으로,
네트워크 스무딩 적용 여부는 게임이 시작할 때의 핑 측정 결과에 따라서가 아닌, 옵션 설정에 따라서로 하기로 했다.
여러가지 이유로 게임이 시작될 때의 핑 환경이 평소보다 좋을 수도, 나쁠 수도 있기 때문.
따라서 옵션 UI를 다음과 같이 만들어주었다.

그리고 활성화/비활성화 콜백 함수는 다음과 같다.
void UISMOptionPanel::SetSmoothCharacterMovement(bool bEnable)
{
if (UISMGameInstance* GI = GetGameInstance<UISMGameInstance>())
{
GI->bEnableSmoothCharacterMovement = bEnable;
GI->SaveGame();
SetNetworkSettingUI();
if (APlayerController* PlayerController = GI->GetFirstLocalPlayerController())
{
if (AISMPlayerController* ISMPlayerController = Cast<AISMPlayerController>(PlayerController))
{
if (AISeeMeCharacter* OtherCharacter = ISMPlayerController->GetOtherCharacter())
{
OtherCharacter->SetSmoothCharacterMovement(bEnable); // 플레이어에게 적용
}
}
}
}
}
OtherCharacter->SetSmoothCharacterMovement(bEnable); 가 위에서 만든 함수를 사용하는 부분이다.
OtherCharacter에 대해 적용하는 것이 핵심이다. 움직임을 부드럽게 하고 싶은 캐릭터는 내 캐릭터가 아니고 상대방 캐릭터이기 때문.
변경사항은 여기까지가 되겠다.
수정사항은 재차 팀장 분과 테스트해서 문제 없는 것을 확인하고, 스팀에 업데이트로 올리게 되었다.

'언리얼 엔진 5 > 멀티플레이어 게임 폴리싱' 카테고리의 다른 글
| [UE5] 멀티플레이어 게임 폴리싱 (3) - 렉 최적화 (회전 에러 수정, 핑 측정 후 조정 기능) (0) | 2025.10.26 |
|---|---|
| [UE5] 멀티플레이어 게임 폴리싱 (2) - 렉 최적화 (카메라, 애니메이션, 보간 시간) (0) | 2025.10.24 |
| [UE5] 멀티플레이어 게임 폴리싱 (1) - 렉 최적화 개요 (0) | 2025.10.16 |