UI와 관련된 작업을 하던 중, 일부 매치메이킹 시스템의 구조에 대해 생각하게 되었습니다.
- 초기 게임모드에서는 빙의되어있는 폰이 필요없다.
- UI를 통한 매치메이킹 시스템 후 선택된 캐릭터를 빙의한다.
- 그렇다면, 게임 시작 로딩을 고려하여 UI만 로딩하는것이 어떨까?
- 프로젝트 설계 시 목적이였던 멀티플레이 환경에서 UGamePlayStatics::OpenLevel는 작동할까?
그 외 다양한 구조를 생각하며 찾아보던 중 플레이어 컨트롤러 내 존재하는 ClientTravel 함수에 대해 알게되었습니다.
이번 포스팅에서는 ClientTravel을 사용한 UI 버튼 클릭 시 레벨 이동하기, 그리고 그와 관련된 다양한 함수들에 대해서 알아보겠습니다.
Level 생성 / 이동 관련 함수
UGameplayStatics::OpenLevel()
- 다양한 기능을 가지고 있는 UGameplayStatics를 사용한 레벨 전환
- 모든 객체를 삭제시킨 후 새로 시작한다.
- 쉬운 작동법 (GetWorld(), "Open Level FString")
- 로컬에서 새 레벨을 로드한다.
- 네트워크 처리를 하지 못해 싱글플레이어 게임 환경에서만 가능하다.
APlayerController::ClientTravel()
- 네트워크 또는 레벨을 이동하는 형식
- 현재 상태를 유지한다. (플레이어 상태, HUD 등)
- 클라이언트에서 호출하는 형식.
- 서버에서 호출되는 경우 특정 클라이언트에게 새 맵으로 이동하라 명령(현재 서버 접속은 유지)
- 멀티플레이 게임의 경우 ClientTravel, ServerTravel을 사용하여 레벨을 이동한다.
- ETravelType를 사용한 이동방식 조절 가능
- ETravelType::TRAVEL_Absolute : 절대경로
- ETravelType::TRAVEL_Relative : 상대경로
- ETravelType::Particle : 현재 맵을 유지하며 새로운 서버 연결 시도
- 서버 연결 시 FString ServerAddress(TEXT("IPv4")를 사용한 이동 가능
- 해당 주소의 서버에서 어떤 레벨을 연결할 지 결정.
- 기본값 127.0.0.1 (로컬 PC 주소)
APlayerController::ServerTravel()
- 서버 전용 레벨 이동
- 서버를 새 월드/레벨로 점프시킨다.
- 서버에 접속되어있는 모든 클라이언트들도 함께 이동한다.
- 모든 클라이언트 플레이어에게 ClientTravel() 호출
- 멀티플레이 환경에서 서버 IPv4주소에 따른 클라이언트 이동시 LevelURL은 서버에서 처리
- Ex)GetWorld()->ServerTravel(LevelURL);
UEngine::Brose()
- 새 맵 로드시 하드리셋같은 개념
- 원활하지 않은 이동 (Non - Seamless)
- 클라이언트는 서버에서 접속을 끊은 다음 같은 서버에 다시 접속하여 새로 로드할 맵을 준비한다.
- 데디케이티드 서버의 경우 다른 서버로 이동할 수 없으므로, 맵은 반드시 로컬이여야한다.
원활한 이동 (Seamless)
- 트랜지션 맵을 구성한 상태에서 사용한다.
- UGameMapsSettings::TransitionMap 프로퍼티를 통해 이루어진다
- AGameModeBase::bUseSeamlessTravel true 설정 추가 필요
- 현재 맵에서 트랜지션 맵으로 이동 후, 최종 맵으로 이동하는 형식
- 새 레벨로 액터를 가지고 가는것이 가능하다.
- 인벤토리 아이템, 플레이어와 같은 특정 액터
원활하지 않은 이동 (Non-Seamless)
- Blocking 형태
- 클라이언트는 접속을 끊은 뒤 서버에 재연결해 맵을 새로 로드한다.
UGameInstance::LoadMap(MapName, Options)
- 현재 게임 상태를 유지하며, 새로운 맵을 로드할 경우 사용
- 멀티플레이 불가
- OpenLevel()보다 유연하게 사용가능, 직관적이지는 않다.
- FString Options를 설정한 특정 설정 전달 가능
ULevelStreaming::LoadLevelInstance()
- 기존 레벨을 유지하며 새로운 레벨을 특정 좌표에 로드하는 기능
- 오픈월드 게임 혹은 레벨 스트리밍이 필요한 상황에서 사용
- 동적으로 레벨을 로드하고 배치 할 수 있다.
- 멀티플레이 불가
- 동적으로 로드되는 객체들을 관리해야한다.
ULevelStreamingDynamic::LoadLevelInstance()
- LoadLevelInstance와 유사하지만, 여러개의 레벨을 로드 할 수 있다.
- 특정 오브젝트 로드 가능
- 객체와 AI관련 추가 설정 필요
함수 | 멀티플레이 | 현재 상태 유지 | 언제 |
OpenLevel | 불가능 | 불가능 | 싱글플레이, 새로시작 |
ClientTravel | 가능 | 가능 | 멀티플레이 환경 보편적 사용 |
Browse | 가능 | 가능 | 새 맵에서의 하드리셋 |
LoadMap | 불가능 | 가능 | 현재 게임상태 유지, 새로운 맵 로드시 |
LoadLevelInstance | 불가능 | 가능 | 오픈월드 |
DynamicLoadLevelInstance | 불가능 | 가능 | 오픈월드, 특정오브젝트 로드, 여러개의 레벨 로드시 |
사용 예시
UUserWidget (상속받은 하위 클래스)
- GameMode, GameState, GameInstance를 사용하여 위젯 AddToViewPort 설정 완료했을 경우
- 플레이어 컨트롤러 내 설정 FInputModeUIOnly || FInputModeGameAndUI설정 완료했을 경우
void UUserWidget::NativeOnInitialized()
{
Super::NativeOnInitialized();
// 입력 허용
bIsFocusable = true;
// 버튼에 대한 OnClicked 이벤트 함수와 바인딩
if (StartButton && !StartButton->OnClicked.IsBound())
{
StartButton->OnClicked.AddDynamic(this, &UMainLobbyWidget::OpenInGameLevel);
}
}
void UUserWidget::OpenInGameLevel()
{
// 이동하려는 레벨 설정
FString LevelName = TEXT("OpenLevelPath");
// 플레이어 컨트롤러가 존재할 경우
if (APlayerController* PC = Cast<APlayerController>(GetOwningPlayer()))
{
if (PC)
{
// 해당 UI는 삭제한다.
RemoveFromParent();
// 이동하려는 레벨,
PC->ClientTravel(LevelName, ETravelType::TRAVEL_Absolute);
// UI는 사용하지 않으니 마우스의 입력을 받을 수 있게 모드 변경
PC->SetInputMode(FInputModeGameOnly());
// 마우스커서 삭제
PC->bShowMouseCursor = false;
}
}
else
{
UE_LOG(LogTemp, Error, TEXT("Player Controll Nullptr"));
}
/* TODO:
현재는 Replication 적용하지 않은 상태
Replicated 추가시 매치매이킹 시스템 구현
1. 서버에 큐 참가 요청
2. 해당 큐에 일정 인원이 모이면 매치 생성
3. 서버에서 해당 매칭 완료 결과를 클라이언트에 전달
--Case : 클라이언트 (로비 UI 내 ) --
4. 클라이언트는 메시지를 응답받고 게임으로 이동. (ClientTravel)
--Case : 서버 (게임 세션 생성, 전환)
4. 새 게임 서버 생성 (ServerTravel)
5. 클라이언트들에게 새로운 게임 서버로 이동 명령(ClientTravel)
현재는 로직 구현을 우선순위로 한 싱글플레이 전용으로 구현
*/
}
아직은 UProperty 내 Replicated를 추가하지 않은 상태의 개발중이며
Replicated, RPC를 사용한 연동 시 TODO 형식으로 사용 예정입니다.
기본적인 로직을 완료 후 싱글플레이 -> 멀티플레이 전환시 Queue를 사용한 매치메이킹 시스템 추가,
ServerTravel을 사용한 Queue 삽입된 플레이어 이동 관련 구현 예정이며, 추후 관련 포스팅 예정입니다.
출처 및 참고자료
https://www.reddit.com/r/unrealengine/comments/1fa5e0h/how_to_let_players_enter_different_levels_in/
https://dawnarc.com/2017/06/ue4networking-in-basic-travelling-in-multiplayer/
'UE5 - Project > 개인 프로젝트 - FPS TPS' 카테고리의 다른 글
[UE5] - UI를 사용한 키리맵핑 (0) | 2025.02.13 |
---|---|
[UE5] - 데이터에셋과 UI 연동하기 (0) | 2025.02.11 |