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://dev.epicgames.com/documentation/ko-kr/unreal-engine/travelling-in-multiplayer-in-unreal-engine

https://www.reddit.com/r/unrealengine/comments/1fa5e0h/how_to_let_players_enter_different_levels_in/

https://upbo.tistory.com/90

https://dawnarc.com/2017/06/ue4networking-in-basic-travelling-in-multiplayer/

+ Recent posts