개발일지 - Unreal Engine RPC

김민오·2022년 8월 4일
1

Unreal Engine RPC(Remote Process Call)

언리얼 엔진에서 RPC는 Replication과 함께 멀티플레이를 구현하기위해서 사용되는 개념중 하나이다.

혼자서 플레이하는 게임을 생각해보자 플레이어가 특정 키를 입력하고 키를 입력받은 캐릭터는 주문을 영창하며 파이어볼을 날리는 스킬을 구현해야한다고 가정한다면 크게 고민할 필요는 없을것이다.

  • 어떤 키를 입력할 것인지
  • 키 입력함수에 바인딩할 Fireball 함수는 어떻게 구현할것인지
  • Spawn된 Fireball 오브젝트는 어떻게 구현할 것인지

대략 이 정도만 고려하면 될 것이다.

그러나 멀티플레이 게임에서는 위의 요소들에 더해 몇가지 추가적으로 고민해야할 부분들이 생기게된다. RPC, Replication같은 개념들은 잠깐만 잊고 위의 로직들을 아무것도 추가하지않은 채로 멀티플레이를 진행한다고 가정해보자.

멀티플레이를 구현한다는 것은 플레이어가(클라이언트, 리슨서버라면 서버가 될 수도 있다) 호출하는 함수가 플레이어 자신의 머신 뿐만 아니라 다른 플레이어의 머신에서도 호출되어야 한다는 것을 의미한다.

클라이언트가 Fireball을 소환하는 함수를 호출하면 그 함수가 서버에서 원격으로 호출되고 동시에 서버는 해당 함수를 다시 연결되어있는 클라이언트들에게 다시 원격으로 호출해준다. 물론 어떻게 구현하느냐에 따라 방식의 차이가 있을 수도 있는데 결국 RPC의 핵심은 그 이름처럼 원격으로 호출되는데에 있다.

언리얼 엔진에서는 Client, Server, Multicast 세 가지 방식으로 RPC를 사용할 수 있다.

다만 RPC를 사용하기전에 누가 어떤 액터를 소유하고 있는지를 파악하는 것이 중요한데 같은 방식의 RPC라고 하더라도 이 함수가 클라이언트에서 실행되는지 혹은 서버에서 실행되는지, 그리고 클라이언트 머신에서 다른 클라이언트가 소유중인 객체에서 실행되는지 아니면 클라이언트 머신에서 서버가 소유중인 객체에서 실행되는지 . . . 열거만해도 머리가 아파온다 이렇게 누가 어떤 액터를 소유하고있는지에 대한 이해가 부족한 상태로 RPC를 사용하다보면 정말 미궁속으로 빨려들어간다...(경험담)

우선 위의 그림에서 Server Player를 하나 더 추가하고 Client1 입장에서 바라본 객체들은 아래와 같다.

이걸 Server 입장에서 바라본다면 또 아래와 같이 볼 수 있다.

어떤 유형의 액터에서 RPC를 호출하느냐에 따라 그 양상은 달라지게되는데 언리얼 도큐먼트에서는 아래의 표를 통해서 보여주고있다.

도입부부터 계속 예시로 들던 Fireball을 Unreal RPC를 통해서 구현해야한다면 아래처럼 생각할 수 있을것이다.

먼저 함수는 3개가 필요할 것이다. 오브젝트를 스폰하고 사운드 이펙트를 재생하는 그런과정들은 일단 생략하겠다.

  • UFUNCTION() void FireBall()
  • UFUNCTION(Server, Reliable) void ServerFireBall()
  • UFUNCTION(NetMulticast, Reliable) void MulticastFireBall()

먼저 키 입력 이벤트에 바인딩할 FireBall() 함수는 내부에서 ServerFireBall()함수를 호출한다.

void AWizard::FireBall()
{
	ServerFireBall();
}

그리고 호출된 ServerFireBall()함수는 다시 내부에서 MulticastFireBall() 함수를 호출한다.


// RPC로 사용할 메서드들은 구현 시 이름 끝에 _Implementation를 반드시 붙여줘야한다.
// 호출시에는 원래 메서드 이름 그대로 사용하면된다.

void AWizard::ServerFireBall_Implementation()
{
	MulticastFireBall();
}

그리고 MulticastFireBall() 함수 내부에서 로직을 구현하면된다.


void AWizard::MulticastFireBall_Implementation()
{
	사운드 이펙트를 재생하고 . . .
    
    파이어볼 오브젝트를 생성하고 . . .
}

이제 함수들이 어떻게 실행되는지를 보면

FireBall()함수가 실행된다, 내부에서 ServerFireBall() Server RPC 함수가 실행된다. 이 때 ServerFireBall()함수를 호출하는 곳은 클라이언트 머신에서 호출하는 클라이언트가 소유중인 액터이다. 해당 소유 위치에서 Server RPC는 서버에서 원격으로 실행된다.

원격으로 호출된 ServerFireBall() 함수는 NetMulticast RPC 함수인 MultiCastFireBall()을 호출한다. 이 때 MultiCastFireBall()을 호출하는 곳은
서버 머신에서 클라이언트가 소유한 액터이며 이 위치에서 NetMulticast는 서버와 모든 클라이언트에서 실행된다.

MultiCastFireBall()의 로직이 실행되면 FireBall 함수가 실행될것이고 모든 머신에서 Client1의 캐릭터는 입에서 불을 뿜게될것이다.

profile
https://github.com/gimhema, https://gimhema.tistory.com/

0개의 댓글