본문 바로가기

Unreal/언리얼 엔진 5로 개발하는 멀티플레이어 게임(Book)

[Unreal] US_Character Class 코드 분석(RPC 달리기)

캐릭터가 달리기 액션을 하면 클라이언트에서 실행되지만, 명령을 내리는 서버에서는 실행되지 않아 문제가 발생한다.

따라서 서버에서 재정의가 업데이트될 때마다 캐릭터의 이동 속도가 느려진다.

 

결국 캐릭터를 달리기 속도로 이동시키려고 하지만, 서버가 클라이언트의 이동을 복제하면 캐릭터는 다시 걷는 속도로 돌아가는 것이다.

 

클라이언트가 이런 중요한 상호작용을 제어하는 것은 원하지 않는다. 명령을 내리는 것은 서버이기 때문에 이 문제를 수정하기 위해 RPC를 사용한다.

US_Character.h

UFUNCTION(Server, Reliable)
void SprintStart_Server();

UFUNCTION(Server, Reliable)
void SprintEnd_Server();

 

UFUNCTION(Server, Reliable)은 클라이언트 PC의 오브젝트가 함수를 호출하지만 동일한 오브젝트의 서버 버전에서만 실행된다. 클라이언트는 함수를 호출하는 오브젝트를 소유해야 한다. 또한 신뢰성을 강제하여 메시지가 목적지에 안전하게 도달하도록 보장하고 있다.

US_Character.cpp

void AUS_Character::SprintStart_Server_Implementation()
{
    if (GetCharacterStats())
    {
        GetCharacterMovement()->MaxWalkSpeed = GetCharacterStats()->SprintSpeed;
    }
}

void AUS_Character::SprintEnd_Server_Implementation()
{
    if (GetCharacterStats())
    {
        GetCharacterMovement()->MaxWalkSpeed = GetCharacterStats()->SprintSpeed;
    }
}

 

위에 해더에서 정의한 함수는 _Implementation 접미사를 추가하여 구현한다.

추가 수정 사항

캐릭터가 달리는 중에 레벨업하면 이동 속도가 걷는 속도로 되돌아간다. 이는 UpdateCharacterStates() 함수에서 캐릭터가 달리는 중이더라도, MaxWalkSpead 프로퍼티를 새로운 걷기 속도로 설정하기 때문이다. 

 

US_Character.h

void AUS_Character::UpdateCharacterStats(int32 CharacterLevel)
{
    auto IsSprinting = false;
    if (GetCharacterStats())
    {
        IsSprinting = GetCharacterMovement()->MaxWalkSpeed == GetCharacterStats()->SprintSpeed;
    }

    ...
}

 

이때는 달리는는 중이라면 달리는 속도로 다시 적용시키면 되기 때문에 위와 같이 달리는 중인지 확인 후

 

US_Character.cpp

void AUS_Character::UpdateCharacterStats(int32 CharacterLevel)
{
    ...
    if (CharacterDataTable)
    {
        TArray<FUS_CharacterStats*> CharacterStatsRows;
        CharacterDataTable->GetAllRows<FUS_CharacterStats>(TEXT("US_Character"), CharacterStatsRows);

        if (CharacterStatsRows.Num() > 0)
        {
            const auto NewCharacterLevel = FMath::Clamp(CharacterLevel, 1, CharacterStatsRows.Num());
            CharacterStats = CharacterStatsRows[NewCharacterLevel - 1];

            if (IsSprinting)
            {
                SprintStart_Server();
            }
        }
    }
}

 

서버에 실행하도록 요청하면 오류는 사라진다.