RPC (Remote Procedure Call)

김지윤·2024년 12월 8일

UE5_MultiplayerShooter

목록 보기
2/25

RPC란 Remote Procedure Call의 약자로, 한 기기가 함수를 호출했을 때 다른 기기가 해당 처리를 대신해 원격 실행하는 함수를 말한다.

이를 이용한 C 입력 시 앉기 애니메이션, 우클릭 시 조준 애니메이션이 실행되는 기능을 구현했다.

애님 블프는 간단하다. bIsCrouched가 true면 CrouchIdle로 넘어가고, bAiming이 true면 그에 따른 조준 애니메이션을 실행한다. 해당 변수는 Character에게서 Tick으로 받아온다.

// bIsCrouched는 언리얼에서 제공되는 변수

UPROPERTY(Replicated)
bool bAiming;
    
void UCombatComponent::SetAiming(bool bIsAiming)
{
	bAiming = bIsAiming;
}

그리고 서버와 클라이언트간 복제될 수 있도록 Replicated 속성도 달아뒀다.

서버가 우클릭을 누른 경우 클라이언트들에게 Replicate되어 클라이언트들의 화면에서도 조준 애니메이션이 출력된다.
하지만 클라이언트가 우클릭을 누른 경우 해당 클라이언트의 화면에서만 조준 애니메이션이 출력되는 문제가 있다.
이는 클라이언트가 아직 서버에게 요청하지 않았기 때문이다.
또한, 클라이언트는 서버의 허락 없이 변수를 마음대로 바꿔버렸다.
이 문제들을 해결하는 게 RPC다.
RPC란 Remote Procedure Call의 약자로, 한 기기가 함수를 호출했을 때 다른 기기가 해당 처리를 대신해 원격 실행하는 함수를 말한다.

UFUNCTION(Server, Reliable)
void ServerSetAiming(bool bIsAiming);void 
    
UCombatComponent::ServerSetAiming_Implementation(bool bIsAiming)
{
	bAiming = bIsAiming;
}

Server 속성을 달면 서버에서 실행되는 함수라는 뜻이다. 클라이언트는 이 함수를 직접 실행할 수 없고, 호출하면 서버에게 요청하게 된다. Reliable 속성은 해당 요청이 반드시 실행되도록 보장해주는 속성이다. (서버가 답장 보내줄 때까지 요청을 계속 보냄)

이제 SetAiming의 로직을 조금 수정하자.

void UCombatComponent::SetAiming(bool bIsAiming)
{
	// 서버인 경우 바로 변경
	if (Character->HasAuthority())
	{
		bAiming = bIsAiming;
	}

	// 클라이언트인 경우 Aim을 서버에 요청
	ServerSetAiming(bIsAiming);
}

HasAuthority를 통해 서버와 클라이언트 중 누가 호출한 건지 파악한다. 서버라면 바로 조준 애니메이션을 실행할 수 있도록 bool의 값을 변경해주었다. 클라이언트라면 ServerSetAiming을 통해 서버에게 요청해 원격으로 변경하도록 했다. 이러면 서버에서 변수가 변경되었기 때문에 변경사항이 요청된 클라이언트를 포함해 모두에게 복제된다.

단, 이러면 또 다른 문제가 있다.
바로 로직의 문제가 아닌 '조작감'의 문제다.
클라이언트는 우클릭을 눌렀을 때 그 즉시 조준이 되는 게 아니고, 요청이 서버에 다녀온 뒤 조준할 수 있다.
즉, 서버와 클라이언트의 연결 지연 시간(핑)의 2배만큼 후에 조준이 된다는 거다.
이는 플레이어 경험에 치명적일 수 있다. 3인칭이라면 조금 덜 할 수 있겠지만 1인칭이라면 굉장히 불쾌한 경험이 된다.

void UCombatComponent::SetAiming(bool bIsAiming)
{
	// 일단 바로 변경, 클라이언트인 경우 아직 서버는 모름
    bAiming = bIsAiming;

	// 클라이언트인 경우 Aim을 서버에 요청
	ServerSetAiming(bIsAiming);
}

따라서 누르는 순간 바로 조준한 후 해당 사항을 서버에게 요청해 다른 클라이언트들도 알 수 있도록 했다.

하지만 이 경우도 문제가 없는 건 아니다.
핑이 매우 높은 환경에서 조준을 '따닥' 실행한 경우, 플레이어는 조준과 조준 취소를 왕복 1회만 했다. 하지만 클라이언트가 직접 bAiming을 true로 바꾼 뒤 false로 바꾸고, 서버로부터 온 응답에 의해 다시 true로 바뀐 뒤 false로 바뀔 것이다. 최종적으로 플레이어는 왕복 2회의 애니메이션 출력을 보게 될 것이다.

DOREPLIFETIME_CONDITION(UCombatComponent, bAiming, COND_SkipOwner);

그 때 쓰는 게 SkipOwner다. 언리얼은 정말 없는 게 없다.
이는 요청한 클라이언트를 제외한 세션 접속자에게 변경 사항을 알리는 복제 조건이다.
이걸로 마지막 문제까지 해결했다.

profile
공부한 거 시간 날 때 작성하는 곳

0개의 댓글