목차

     

     


     

     

    Multiplayer 10: Lobby 레벨 기본설정 및 변경하기

     

     


    Menu

     

    Menu.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Blueprint/UserWidget.h"
    #include "Interfaces/OnlineSessionInterface.h"//EOnJoinSessionCompleteResult 사용을 위한 헤더
    #include "Menu.generated.h"
    
    UCLASS()
    class MULTIPLAYERSESSIONS_API UMenu : public UUserWidget
    {
    	GENERATED_BODY()
    
    public:
    	UFUNCTION(BlueprintCallable)
    		void MenuSetup(int32 NumberOfPublicConnections = 4, FString TypeOfMatch = FString(TEXT("FreeForAll")), FString LobbyPath = FString(TEXT("/Game/ThirdPerson/Maps/Lobby")));//접속할 수 있는 Player 숫자를 input으로 만들고 4를 기본값으로 설정한다. Lobby 레벨을 기본으로 설정한다.
    
    protected:
    
    	virtual bool Initialize() override;
    	virtual void OnLevelRemovedFromWorld(ULevel* InLevel, UWorld* InWorld) override;//다른 Level로 이동하면 기존의 Level을 지우는 함수.
    
    	//
    	// Callbacks for the custom delegates on the MultiplayerSessionsSubsystem
    	// MultiplayerSessionsSubsystem.h의 Dynamic Delegate과 연결된다. Dynamic과 연결되는 경우 UFUNCTION()을 꼭 붙여야 한다. 붙이지 않으면 dynamic delegate과 연결되지 않는다.
    	UFUNCTION()
    	void OnCreateSession(bool bWasSuccessful);
    	void OnFindSessions(const TArray<FOnlineSessionSearchResult>& SessionResults, bool bWasSuccessful);
    	void OnJoinSession(EOnJoinSessionCompleteResult::Type Result);
    	UFUNCTION()
    	void OnDestroySession(bool bWasSuccessful);
    	UFUNCTION()
    	void OnStartSession(bool bWasSuccessful);
    
    private:
    	//meta = (BindWidget)을 사용하면 BP의 Button Widget이 아래의 변수와 연결된다. 이 때 BP와 C++변수 이름은 일치하여야 한다.
    	UPROPERTY(meta = (BindWidget))
    		class UButton* HostButton;
    
    	UPROPERTY(meta = (BindWidget))
    		UButton* JoinButton;
    
    	UFUNCTION()
    		void HostButtonClicked();
    
    	UFUNCTION()
    		void JoinButtonClicked();
    
    	void MenuTearDown();
    
    	// The subsystem designed to handle all online session functionality
    	class UMultiplayerSessionsSubsystem* MultiplayerSessionsSubsystem;
    	
    	int32 NumPublicConnections{ 4 };
    	FString MatchType{ TEXT("FreeForAll") };
    	FString PathToLobby{ TEXT("") };
    };

    void MenuSetup 함수에 매개변수 추가

    • void MenuSetup(int32 NumberOfPublicConnections = 4, FString TypeOfMatch = FString(TEXT("FreeForAll")), FString LobbyPath = FString(TEXT("/Game/ThirdPerson/Maps/Lobby")));
      • 접속할 수 있는 Player 숫자를 input으로 만들고 4를 기본값으로 설정한다. 입장하는 레벨 맵을 Lobby로 기본으로 설정한다.

     

     

     

    Menu.cpp

    더보기
    #include "Menu.h"
    #include "Components/Button.h"
    #include "MultiplayerSessionsSubsystem.h"
    #include "OnlineSessionSettings.h"
    #include "OnlineSubsystem.h"
    
    void UMenu::MenuSetup(int32 NumberOfPublicConnections, FString TypeOfMatch, FString LobbyPath)
    {
    	PathToLobby = FString::Printf(TEXT("%s?listen"), *LobbyPath);
    	NumPublicConnections = NumberOfPublicConnections;
    	MatchType = TypeOfMatch;
    	AddToViewport();
    	SetVisibility(ESlateVisibility::Visible);
    	bIsFocusable = true;
    
    	UWorld* World = GetWorld();
    	if (World)
    	{
    		APlayerController* PlayerController = World->GetFirstPlayerController();
    		if (PlayerController)
    		{
    			FInputModeUIOnly InputModeData;
    			InputModeData.SetWidgetToFocus(TakeWidget());
    			InputModeData.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
    			PlayerController->SetInputMode(InputModeData);
    			PlayerController->SetShowMouseCursor(true);
    		}
    	}
    
    	UGameInstance* GameInstance = GetGameInstance();
    	if (GameInstance)
    	{
    		//헤더에서 만든 MultiplayerSessionsSubsystem 변수에 Subsystem을 리턴 값을 넣어준다.
    		MultiplayerSessionsSubsystem = GameInstance->GetSubsystem<UMultiplayerSessionsSubsystem>();
    	}
    
    	if (MultiplayerSessionsSubsystem)
    	{
    		//callback을 Bind해준다.
    		MultiplayerSessionsSubsystem->MultiplayerOnCreateSessionComplete.AddDynamic(this, &ThisClass::OnCreateSession);//Dynamic을 Bind하는 경우는 AddDynamic 사용.
    		MultiplayerSessionsSubsystem->MultiplayerOnFindSessionsComplete.AddUObject(this, &ThisClass::OnFindSessions);//Dynamic이 아닌 것을 Bind하는 경우는 AddUObject 사용.
    		MultiplayerSessionsSubsystem->MultiplayerOnJoinSessionComplete.AddUObject(this, &ThisClass::OnJoinSession);
    		MultiplayerSessionsSubsystem->MultiplayerOnDestroySessionComplete.AddDynamic(this, &ThisClass::OnDestroySession);
    		MultiplayerSessionsSubsystem->MultiplayerOnStartSessionComplete.AddDynamic(this, &ThisClass::OnStartSession);
    	}
    }
    
    bool UMenu::Initialize()
    {
    	//Super버젼이 false면 false를 리턴한다.(=상위 클래스에서 Initialize 하는게 없으면 false를 리턴하여 Initialize()를 하지 않는다.)
    	if (!Super::Initialize())
    	{
    		return false;
    	}
    
    	if (HostButton)
    	{
    		//HostButton 클릭할 때 발생하는 이벤트를 Delegate로 연결해준다.
    		HostButton->OnClicked.AddDynamic(this, &ThisClass::HostButtonClicked);
    	}
    	if (JoinButton)
    	{
    		//JoinButton 클릭할 때 발생하는 이벤트를 Delegate로 연결해준다.
    		JoinButton->OnClicked.AddDynamic(this, &ThisClass::JoinButtonClicked);
    	}
    
    	return true;
    }
    
    void UMenu::OnLevelRemovedFromWorld(ULevel* InLevel, UWorld* InWorld)
    {
    	MenuTearDown();//Widget을 뷰포트에서 지우고 input mode가 리셋된다.
    	Super::OnLevelRemovedFromWorld(InLevel, InWorld);
    }
    
    void UMenu::OnCreateSession(bool bWasSuccessful)
    {
    	if (bWasSuccessful)
    	{
    		if (GEngine)
    		{
    			GEngine->AddOnScreenDebugMessage(
    				-1,
    				15.f,
    				FColor::Yellow,
    				FString(TEXT("Session created successfully!"))
    			);
    		}
    
    		UWorld* World = GetWorld();
    		if (World)
    		{
    			//World->ServerTravel("/Game/ThirdPerson/Maps/Lobby?listen");
    			World->ServerTravel(PathToLobby);
    		}
    	}
    	else//실패한 경우
    	{
    		if (GEngine)
    		{
    			GEngine->AddOnScreenDebugMessage(
    				-1,
    				15.f,
    				FColor::Red,
    				FString(TEXT("Failed to create session!"))
    			);
    		}
    	}
    }
    
    void UMenu::OnFindSessions(const TArray<FOnlineSessionSearchResult>& SessionResults, bool bWasSuccessful)
    {
    	if (MultiplayerSessionsSubsystem == nullptr)
    	{
    		return;
    	}
    
    	for (auto Result : SessionResults)//변수로 받은 SessionResults배열을 순회하며 검색
    	{
    		FString SettingsValue;
    		Result.Session.SessionSettings.Get(FName("MatchType"), SettingsValue);
    		//SessionSettings의 결과값이 "MatchType"과 SetingsValue을 가지고 있는지 확인.
    		if (SettingsValue == MatchType)
    		{
    			MultiplayerSessionsSubsystem->JoinSession(Result);
    			return;
    		}
    	}
    }
    
    void UMenu::OnJoinSession(EOnJoinSessionCompleteResult::Type Result)
    {
    	//IOnlineSubsystem 사용을 위해 위에 #include "OnlineSubsystem.h" 추가
    	IOnlineSubsystem* Subsystem = IOnlineSubsystem::Get();
    	if (Subsystem)
    	{
    		IOnlineSessionPtr SessionInterface = Subsystem->GetSessionInterface();
    		if (SessionInterface.IsValid())
    		{
    			FString Address;
    			SessionInterface->GetResolvedConnectString(NAME_GameSession, Address);
    
    			APlayerController* PlayerController = GetGameInstance()->GetFirstLocalPlayerController();
    			if (PlayerController)
    			{
    				PlayerController->ClientTravel(Address, ETravelType::TRAVEL_Absolute);
    			}
    		}
    	}
    }
    
    void UMenu::OnDestroySession(bool bWasSuccessful)
    {
    }
    
    void UMenu::OnStartSession(bool bWasSuccessful)
    {
    }
    
    void UMenu::HostButtonClicked()
    {
    	//디버깅용
    	if (GEngine)
    	{
    		GEngine->AddOnScreenDebugMessage(
    			-1,
    			15.f,
    			FColor::Yellow,
    			FString(TEXT("Host Button Clicked"))
    		);
    	}
    
    	if (MultiplayerSessionsSubsystem)
    	{
    		MultiplayerSessionsSubsystem->CreateSession(NumPublicConnections, MatchType);//NumPublicConnections 숫자 크기까지 Players 접속 가능
    		UWorld* World = GetWorld();
    	}
    }
    
    void UMenu::JoinButtonClicked()
    {
    	//디버깅 테스트
    	if (GEngine)
    	{
    		GEngine->AddOnScreenDebugMessage(
    			-1,
    			15.f,
    			FColor::Yellow,
    			FString(TEXT("Join Button Clicked"))
    		);
    	}
    
    	if (MultiplayerSessionsSubsystem)
    	{
    		//Steam DevID의 숫자가 클수도 있으니 MaxSearchResults값을 10000으로 넉넉히 잡는다.
    		MultiplayerSessionsSubsystem->FindSessions(10000);
    	}
    }
    
    void UMenu::MenuTearDown()
    {
    	RemoveFromParent();
    	UWorld* World = GetWorld();
    	if (World)
    	{
    		APlayerController* PlayerController = World->GetFirstPlayerController();
    		if (PlayerController)
    		{
    			FInputModeGameOnly InputModeData;
    			PlayerController->SetInputMode(InputModeData);
    			PlayerController->SetShowMouseCursor(false);
    		}
    	}
    }

    void UMenu::MenuSetup(int32 NumberOfPublicConnections, FString TypeOfMatch, FString LobbyPath)

    • PathToLobby = FString::Printf(TEXT("%s?listen"), *LobbyPath);
    •     if (World)
              {
                  World->ServerTravel(PathToLobby);
              }

     

     


     

     

    ThirdPersonMap의 Level Blueprint 확인

     

    cpp에서 작업한 Menu Setup이 업데이트되서 들어온다.

     


     

     

    Package Project

     

     

     


     

     

    다른 컴퓨터에서 .exe 파일 열기