1. 캐릭터 Death 처리
흐름
- StatComponent의 OnOutOfCurrentHP -> PlayerCharacter와 Delegate 바인딩
- PlayerCharacter -> PlayerController
- PlayerController -> GameMode
코드
//게임 모드
void ADXGameModeBase::OnCharacterDead(ADXPlayerController* InController)
{
if (!IsValid(InController) || AlivePlayerControllers.Find(InController) == INDEX_NONE)
{
return;
}
AlivePlayerControllers.Remove(InController);
DeadPlayerControllers.Add(InController);
}
//플레이어 컨트롤러
void ADXPlayerController::OnCharaterDead()
{
ADXGameModeBase* GameMode = Cast<ADXGameModeBase>(UGameplayStatics::GetGameMode(this));
//server
if (HasAuthority() && IsValid(GameMode))
{
GameMode->OnCharacterDead(this);
}
}
//플레이어 캐릭터
void ADXPlayerCharacter::BeginPlay()
{
...
StatusComponent->OnOutOfCurrentHP.AddUObject(this, &ThisClass::OnDeath);
}
void ADXPlayerCharacter::OnDeath()
{
ADXPlayerController* PlayerController = GetController<ADXPlayerController>();
//server
if (IsValid(PlayerController) && HasAuthority())
{
PlayerController->OnCharaterDead();
}
}
2. 게임 종료 UI 출력
PlayerController에서 Client RPC 함수를 통해 구현
- 각 캐릭터마다 각각의 UI를 출력하기 때문에 Client RPC로 구현하기
코드
void ADXPlayerController::ClientRPCShowGameResultWidget_Implementation(int32 InRanking)
{
if (IsLocalController())
{
if (IsValid(GameResultUIClass))
{
UUW_GameResult* GameResultUI = CreateWidget<UUW_GameResult>(this, GameResultUIClass);
if (IsValid(GameResultUI))
{
GameResultUI->AddToViewport(3);
FString GameResultString = FString::Printf(TEXT("%s"), InRanking == 1 ? TEXT("Winner Winner Chicken Dinner!") : TEXT("Better Luck Next Time!"));
GameResultUI->ResultText->SetText(FText::FromString(GameResultString));
FString RankingString = FString::Printf(TEXT("#%02d"), InRanking);
GameResultUI->RankingText->SetText(FText::FromString(RankingString));
FInputModeUIOnly Mode;
Mode.SetWidgetToFocus(GameResultUI->GetCachedWidget());
SetInputMode(Mode);
bShowMouseCursor = true;
}
}
}
}
GameMode에 UI를 출력해주는 로직 추가
void ADXGameModeBase::OnMainTimerElapsed()
{
ADXGameStateBase* DXGameState = GetGameState<ADXGameStateBase>();
if (!IsValid(DXGameState))
{
return;
}
switch (DXGameState->MatchState)
{
...
case EMatchState::Playing:
{
...
if (DXGameState->AlivePlayerControllerCount <= 1)
{
DXGameState->MatchState = EMatchState::Enging;
//UI출력
AlivePlayerControllers[0]->ClientRPCShowGameResultWidget(1);
}
break;
}
...
}
}
void ADXGameModeBase::OnCharacterDead(ADXPlayerController* InController)
{
if (!IsValid(InController) || AlivePlayerControllers.Find(InController) == INDEX_NONE)
{
return;
}
//UI 출력
InController->ClientRPCShowGameResultWidget(AlivePlayerControllers.Num());
AlivePlayerControllers.Remove(InController);
DeadPlayerControllers.Add(InController);
}
3. Title 레벨로 나가기
흐름
- GameResult UI에 버튼 추가
- OpenLevel 함수를 통해 Title 레벨로 이동
더보기
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "UW_GameResult.generated.h"
class UTextBlock;
class UButton;
UCLASS()
class SCC_DEDICATEDX_API UUW_GameResult : public UUserWidget
{
GENERATED_BODY()
protected:
virtual void NativeConstruct() override;
private:
UFUNCTION()
void OnReturnToTitleButtonClicked();
public:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (BindWidget))
TObjectPtr<UTextBlock> ResultText;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (BindWidget))
TObjectPtr<UTextBlock> RankingText;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (BindWidget))
TObjectPtr<UButton> ReturnToTitleButton;
};
#include "UI/UW_GameResult.h"
#include "Components/Button.h"
#include "Kismet/GameplayStatics.h"
void UUW_GameResult::NativeConstruct()
{
Super::NativeConstruct();
if (!ReturnToTitleButton.Get()->OnClicked.IsAlreadyBound(this, &ThisClass::OnReturnToTitleButtonClicked))
{
ReturnToTitleButton.Get()->OnClicked.AddDynamic(this, &ThisClass::OnReturnToTitleButtonClicked);
}
}
void UUW_GameResult::OnReturnToTitleButtonClicked()
{
UGameplayStatics::OpenLevel(GetWorld(), FName(TEXT("Title")), true);
}
4. 플레이어 강제 추방
PlayerController에 Client RPC를 통해서 Title로 돌아가는 함수 구현
void ADXPlayerController::ClientRPCReturnToTitle_Implementation()
{
//only client's level change
if (IsLocalController())
{
UGameplayStatics::OpenLevel(GetWorld(), FName(TEXT("Title")), true);
}
}
이후 GameMode에서 일정 시간 이후 해당 함수가 호출되도록 구현
- 참고) 타이머의 Invalidate() 함수를 통해 타이머를 무효화
case EMatchState::Enging:
{
FString NotificationString = FString::Printf(TEXT("Waiting %d for returning to title."), RemainWaitingTimeForEnding);
NotifyToAllPlayer(NotificationString);
--RemainWaitingTimeForEnding;
if (RemainWaitingTimeForEnding <= 0)
{
for (auto AliveController : AlivePlayerControllers)
{
AliveController->ClientRPCReturnToTitle();
}
for (auto DeadController : DeadPlayerControllers)
{
DeadController->ClientRPCReturnToTitle();
}
MainTimerHandle.Invalidate();
return;
}
break;
}
버튼과 타이머로 로비로 추방하는 모습
5. 서버 초기화
'Unreal Engine' 카테고리의 다른 글
Unreal Engine - 데디케이티드 서버 8 (게임 흐름) (0) | 2025.05.02 |
---|---|
Unreal Engine - AI (2) (0) | 2025.04.29 |
Unreal Engine - AI (1) (1) | 2025.04.25 |
Unreal Engine - 멀티플레이 네트워크 최적화 2 (0) | 2025.04.23 |
Unreal Engine - 멀티플레이 네트워크 최적화 1 (0) | 2025.04.22 |