본문 바로가기
언리얼엔진 개발/Network

[UnrealEngine5]네트워크 게임 만들기 What the box(3) 위젯 만들기(2)

by yeni_0224 2023. 5. 8.
728x90
반응형

WidgetSwitcher을 사용해서 여러 페이지에 들어갈 위젯들을 한 클래스 안에서 작업해주었기 때문에

클래스 안에서 변수와 함수를 만들 때 잘 구분해가면서 작업할 필요가 있다. 

작업해야할 변수나 함수 찾는게 헷갈려서 작업시간이 지체된 적이 많았다. 아래의 글을 보며 함께 보는 걸 추천한다.

https://yeni-0224.tistory.com/entry/UnrealEngine5-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EA%B2%8C%EC%9E%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-What-the-box3-%EC%9C%84%EC%A0%AF-%EB%A7%8C%EB%93%A4%EA%B8%B01

 

[UnrealEngine5] 네트워크 게임 만들기 What the box(3) 위젯 만들기(1)

위젯의 중요성 : 게임을 진행시키고, 나의 생각도 진행시킨다. 버튼을 누르면 다음 페이지로 넘어가도록 하는 기능들을 넣어볼 것이다. 그렇다면 일단 위젯을 만들어야겠지? 이것을 위해 사용할

yeni-0224.tistory.com

protected:	
	virtual void NativeConstruct() override;

public:
	UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, meta = (BindWidget))
		class UEditableText* editText_PlayerName;
	UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, meta = (BindWidget))
		class UButton* btn_QuickPlay;
	UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, meta = (BindWidget))
		class UButton* btn_CreateRoom;
	UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, meta = (BindWidget))
		class UButton* btn_BackToMenu;
	class UEnteranceWidget* enteranceWidget;

private:
	UFUNCTION()
		void QuickPlay();
	UFUNCTION()
		void ClickCreateRoom();
	UFUNCTION()
		void BackToMenu();

맨 첫번째 페이지를 위한 변수와 함수이고, 그 다음 페이지를 위한 변수와 함수이다.

주석 필수 매우 필수

UEditableTextBlock* , USlider* , UScrollBox* , UButton* 클래스를 사용해주었다.

public:
		UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, meta = (BindWidget))
		class UButton* btn_Play;
		UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, meta = (BindWidget))
		class UButton* btn_Options;
		UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, meta = (BindWidget))
		class UWidgetSwitcher* widgetSwitcher;
		UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, meta = (BindWidget))
			class UScrollBox* sbox_RoomList;
		UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, meta = (BindWidget))
		class UButton* btn_KoreaServer;

	/*Room Creation Widget*/
		UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, meta = (BindWidget))
			class UEditableText* editText_RoomName;
		UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, meta = (BindWidget))
			class USlider* slider_MatchDurations;
		UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, meta = (BindWidget))
			class USlider* slider_MaxPlayers;
		UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, meta = (BindWidget))
			class UTextBlock* text_MatchDurations;
		UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, meta = (BindWidget))
			class UTextBlock* text_MaxPlayersCount;
		UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, meta = (BindWidget))
			class UButton* btn_CreateRoom02;
		UPROPERTY(VisibleDefaultsOnly, BlueprintReadWrite, meta = (BindWidget))
			class UButton* btn_CreateRoomCancel;

		UPROPERTY(EditAnywhere)
	TSubclassOf<class USessionSlotWidget> searchListSlot;
		class UWTBoxGameInstance* gameInstance;
		UPROPERTY(meta = (BindWidget))
			int32 index;
private:
	UFUNCTION()
	void ClickPlay();
	UFUNCTION()
	void CancelCreation();
	UFUNCTION()
	void OnMovePlayerSlider(float value);
	UFUNCTION()
	void OnMoveDurationSlider(float value);
	UFUNCTION()
	void RefreshList();
	UFUNCTION()
	void CreateSession();
	UFUNCTION()
	void AddRoomSlot(FSessionInfo sessionInfo);

이제 구현해볼 차례

어떤 버튼을 눌렀을 때 어떤 행위를 발생시킬지 Native Construct에 구현해주었다.

헤더에 선언할 때 UFUNCTION()꼭 잊지말고 적어주길 바란다 :)

void UEnteranceWidget::NativeConstruct()
{
	Super::NativeConstruct();

	editText_PlayerName->SetText(FText::FromString(""));

	btn_BackToMenu->OnClicked.AddDynamic(this, &UEnteranceWidget::BackToMenu);
	btn_QuickPlay->OnClicked.AddDynamic(this, &UEnteranceWidget::QuickPlay);
	btn_CreateRoom->OnClicked.AddDynamic(this, &UEnteranceWidget::ClickCreateRoom);

	btn_Play->OnClicked.AddDynamic(this, &UEnteranceWidget::ClickPlay);
	editText_RoomName->SetText(FText::FromString(""));
	text_MatchDurations->SetText(FText::FromString("1"));
	text_MaxPlayersCount->SetText(FText::FromString("2"));
	slider_MatchDurations->OnValueChanged.AddDynamic(this, &UEnteranceWidget::OnMoveDurationSlider);
	slider_MaxPlayers->OnValueChanged.AddDynamic(this, &UEnteranceWidget::OnMovePlayerSlider);
	btn_CreateRoomCancel->OnClicked.AddDynamic(this, &UEnteranceWidget::CancelCreation);
	btn_CreateRoom02->OnClicked.AddDynamic(this, &UEnteranceWidget::CreateSession);

	gameInstance = Cast<UWTBoxGameInstance>(GetGameInstance());
	if(nullptr != gameInstance)
	{
		gameInstance->searchResultDele.AddDynamic(this, &UEnteranceWidget::AddRoomSlot);
	}
}

text 해줘야하는 부분에서는 FText::FromString(" ")을 사용해서 초기화 작업을 해주어 쓰레기값이 들어오지 않도록 막아주자! 게임인스턴스를 받아올 때는 GetGameInstance() 사용해서 받아오도록 하자. delegate 정보들을 instance를 통해 받아올 것이다.

void UEnteranceWidget::QuickPlay()
{
	if (!(editText_PlayerName->GetText().IsEmpty()))
	{
		gameInstance->sessionID = FName(*editText_PlayerName->GetText().ToString());
		UGameplayStatics::OpenLevel(GetWorld(), FName("KHJTestMap"));
	}
}

void UEnteranceWidget::ClickCreateRoom()
{
	if(!(editText_PlayerName->GetText().IsEmpty()))
	{
		widgetSwitcher->SetActiveWidgetIndex(2);
		gameInstance->sessionID = FName(*editText_PlayerName->GetText().ToString());
	}	
}

어떤 맵으로 넘어가고자 할 때 사용하는 양식. 들어갈 session에 대한 정보를 여기서 받아줄 것이다.

일단 지정된 맵으로 들어갈 수 있도록 맵을 강제로 지정해주었다. 2주동안 구현해야할 부분이 너무 많기 때문에 일단 접속이 가능하도록만 적용해주었다. 마감기한을 지키는 것도 매우 중요하기 때문

그리고 버튼을 눌렀을 때 다른 화면으로 넘어갈 수 있도록 하는 양식이다. 텍스트 적는 공간에 아무것도 적혀있지 않으면 넘어가지 않도록 방어해주었다.

void UEnteranceWidget::AddRoomSlot(FSessionInfo sessionInfo)
{
	USessionSlotWidget* searchSlotListWidget = CreateWidget<USessionSlotWidget>(this, searchListSlot);
	if (searchSlotListWidget != nullptr)
	{
		searchSlotListWidget->text_RoomName->SetText(FText::FromString(sessionInfo.roomName));
		searchSlotListWidget->text_GameMode->SetText(FText::FromString("DeathMatch"));
		searchSlotListWidget->text_MemberCount->SetText(FText::FromString(FString::Printf(TEXT("%d / %d"), sessionInfo.currentPlayers, sessionInfo.maxPlayers)));
		searchSlotListWidget->text_GameTime->SetText(FText::FromString(FString::Printf(TEXT("%d"), sessionInfo.gamePlayTime)));
		searchSlotListWidget->text_Description->SetText(FText::AsNumber(sessionInfo.ping));
		searchSlotListWidget->index = sessionInfo.index;
		sbox_RoomList->AddChild(searchSlotListWidget);
	}
}

검색했을 때 뜨는 방 목록들을 띄우기 위한 작업이다. AddChild를 통해 각 방 정보가 담긴 slot들이 리스트로 추가될 수 있도록 해준 것이다.

void UEnteranceWidget::OnMovePlayerSlider(float value)
{
	int32 val = FMath::RoundHalfFromZero(value);
	text_MaxPlayersCount->SetText(FText::AsNumber(val));
}

슬라이더를 통해서 플레이어의 수를 정하는 부분이다. 혹시 모를 실수가 입력되었을 때 반올림한 값으로 지정되도록 함수로 값을 제한해주었다. 

void UEnteranceWidget::RefreshList()
{
	sbox_RoomList->ClearChildren();
	gameInstance->FindwtbSessions();
}

방을 만들려다 만들지 않고 다시 방 목록들이 있는 페이지로 넘어왔을 때나, 이미 삭제된 방들이 다시 뜨지 않도록 방 목록들을 정리해주는 작업이다.

/*Room Creation Widget에서 CreateRoom버튼 눌렀을 때*/
void UEnteranceWidget::CreateSession()
{
	if(gameInstance != nullptr)
	{
		//플레이어 수
		int32 playerCounts = FMath::RoundHalfFromZero(slider_MaxPlayers->GetValue());
		//플레이 시간
		int32 maxDurations = FMath::RoundHalfFromZero(slider_MatchDurations->GetValue());
		gameInstance->CreatewtboxSession(editText_RoomName->GetText().ToString(), playerCounts, maxDurations);
	}	
}

지정된 플레이어의 수와 시간을 바탕으로 세션을 생성하고자 할 때 사용한 함수인것으로 기억한다.

 

create 버튼이 여러개였고, create가 들어간 함수도 여러개였던데다, 네트워크 작업에 대한 개념이 생소해 많이 헷갈렸던 작업이자, 적응하는데 시간이 많이 걸렸던 작업인듯 하다. 보기엔 너무 간단해보여서 킹받는다.

이렇게 다른 페이지들에 들어갈 위젯들에도 작업해주었다.

이젠 동기화의 지옥에 빠질 예정

728x90
반응형