개발일지/UE5

UE5 멀티플레이 모듈 개발

김진우 개발일지 2024. 1. 9. 02:35

Git

레포지토리 주소 : https://github.com/kjinwoo12/UE5_MultiplayLibrary
기준 태그 : V1.0.1

본문

리그오브워치 개발 중에 멀티플레이 관련 코드를 작성하게 되었다. [리그오브워치개발일지] #12-2 멀티플레이 전용 게임모드 클래스 추가에 대한 추가 글이다.

개발 필요성

플레이어가 서버에 접속했을 때 클라이언트에서 함수가 실행되는 동안 GameStatePlayerState가 유효하다는 보장이 없다. 클라이언트에서 GameState 또는 PlayerStateBeginPlay가 호출되기 전에 다른 액터의 BeginPlay 또는 RPC 가 실행될 가능성이 있다는 뜻이다. 문제를 해결하기 위해서는 GameStatePlayerStateBeginPlay가 호출된 시점을 알려서 원하는 기능이 실행될 시점을 뒤로 미뤄야 한다.
로컬 플레이어의 PlayerState는 간단한 검사를 통해 클라이언트에서 유효성을 보장받지만, 다른 모든 플레이어의 PlayerState가 유효한지 알기 위해서는 서버에 몇 명이 있는지 클라이언트로 알려야한다. 플레이 도중에 플레이어가 접속 또는 로그아웃을 할 가능성이 있으니 관련 처리를 위한 이벤트도 필요하다.
이런 문제는 클라이언트가 서버로 접속한 직후에 동작하는 멀티플레이 관련 기능에서 많이 발생한다. 문제를 해결하고 모듈로 만들어 중복된 코드를 줄일 필요가 있다.

요구사항

  • 클라이언트에서 GameState가 유효해지는 시점을 이벤트로 구현할 수 있어야 한다.
  • 클라이언트에서 PlayerState가 유효해지는 시점을 이벤트로 구현할 수 있어야 한다.
  • 클라이언트는 GameStatePlayerState가 유효해진 시점을 서버로 알려야 한다.
  • 서버에서는 PlayerController를 통해 클라이언트의 GameState::PlayerArray가 서버와 동일한지 확인할 수 있어야 한다.

구현

서버의 이벤트는 IMultiplayServerEvent 인터페이스, 클라이언트의 이벤트는 IMultiplayClientEvent 인터페이스를 통해 구현할 수 있도록 만든다. GameState::BeginPlay()PlayerState::BeginPlay()에서 클라이언트 이벤트를 호출하고, IMMultiplayClientEvnet 인터페이스를 상속받은 PlayerController의 RPC를 통해 IMultiplayServerEvent 인터페이스를 상속받은 GameMode에 이벤트를 호출해 서버에서 클라이언트가 유효해진 시점을 확인할 수 있도록 만든다. 클라이언트에서 유효한 PlayerState가 바뀔 때마다 유효한 PlayerState 목록을 서버로 전달한다면 서버에서 GameState::PlayerArray 와 전달된 목록을 비교하여 해당 PlayerController가 서버와 동기화 되었는지 확인할 수 있게 된다.
실질적인 구현은 Git 레포지토리를 확인하면 된다. 플러그인 형태로 만들어서 만들고 있는 프로젝트에 추가하면 바로 사용할 수 있다.

사용법

플러그인에서 구현된 MultiplayGameMode, MultiplayGameState, MultiplayPlayerState를 상속하면 MultiplayServerEvent 인터페이스와 MultiplayClientEvent의 이벤트가 호출된다. 4번째 요구사항인 동기화 여부는 MultiplayPlayerController::HasAllSyncedPlayerStates() 함수를 사용해서 알 수 있다. 단, 클라이언트에서는 GameState::PlayerArray가 항상 서버보다 동기화가 늦기 때문에 최신 여부를 확인할 수 없고, IMultiplayClientEvent::LocalPlayerStateBegin, IMultiplayClientEvent::OtherPlayerStateBegin 이벤트로 받아오는 PlayerState가 최신임을 가정하고 구현해야 한다.