* HUD UI 오른쪽 상당에 위치한 게임의 스코어 부분을 구현해본다

* 게임을 처음 시작하면 입장한 플레이어는 0의 점수를 가진다. 문을 통과해 새롭게 생성된 섹션에서 나타난 NPC를 처치하면 플레이어는 1의 점수를 획득한다. 플레이어는 레벨을 계속 탐험하면서 NPC를 처치하고 점수를 획득하는데, 이 점수는 플레이어의 점수를 의미하면서 동시에 게임스코어라고도 할 수 있다. 

* 멀티플레이 콘텐츠를 기획한다면, 플레이어에 설정된 데이터 외에도 게임의 데이터를 관리하는 기능을 추가로 고려해야 한다. 언리얼 엔진은 이를 관리하도록 게임스테이트라는 클래스를 제공한다. 

* 학습을 위해 게임 스테이트를 사용해 게임데이터만 분리함으로 관리하는 기능을 추가해본다.

* GameStateBase 기반의 클래스를 생성한다.

* 전체 게임 스코어를 저장하는 속성을 추가한다.

 

//class ABGameState
//h
#include "ArenaBattle.h"
#include "GameFramework/GameStateBase.h"
#include "ABGameState.generated.h"

UCLASS()
class ARENABATTLE_API AABGameState : public AGameStateBase
{
	GENERATED_BODY()
	

public:
	AABGameState();

public:
	int32 GetTotalGameScore() const;
	void AddGameScore();

private:
	UPROPERTY(Transient)
	int32 TotalGameScore;
};

//cpp
#include "ABGameState.h"

AABGameState::AABGameState()
{
	TotalGameScore = 0;
}

int32 AABGameState::GetTotalGameScore() const
{
	return TotalGameScore;
}

void AABGameState::AddGameScore()
{
	TotalGameScore++;
}
//AABGameMode.cpp
#include "ABGameMode.h"
#include "ABCharacter.h"
#include "ABPlayerController.h"
#include "ABPlayerState.h"
#include "ABGameState.h"


AABGameMode::AABGameMode()
{
....
	GameStateClass = AABGameState::StaticClass();
}

 

* 이번에는 섹션 액터의 로직으로 이동해 섹션에서 생성한 NPC가 플레이어에 의해 제거되면, 이를 감지해 섹션 액터의 스테이트를 COMPLETE로 변경하는 기능을 추가한다.

* NPC가 제거될 떄 마지막으로 대미지를 입힌 컨트롤러의 기록은 LastHitBy 속성에 저장된다. 이를 사용하면 액터가 제거될 떄 마지막에 피격을 가한 플레이어의 정보를 바로 얻어올 수 있다.

* 이전에 구현한 경험치의 경우 대미지를 입을 떄마다 Instigator를 검사하는 방식보다 소멸될 떄 LastHitBy를 사용해 처리하는 것이 효율적이지만, 학습을 위해 두가지 모두 공부

 

//ABSection.h
UCLASS()
class ARENABATTLE_API AABSection : public AActor
{
private:
UFUNCTION()
	void OnKeyNPCDestroyed(AActor* DestoryedActor);
}
//ABSection.cpp
#include "ABPlayerController.h"

void AABSection::OnNPCSpawn()
{
	GetWorld()->GetTimerManager().ClearTimer(SpawnNPCTimerHandle);
	auto KeyNPC = GetWorld()->SpawnActor<AABCharacter>(GetActorLocation() + FVector::UpVector * 88.0f, FRotator::ZeroRotator);
	if (nullptr != KeyNPC)
	{
		KeyNPC->OnDestroyed.AddDynamic(this, &AABSection::OnKeyNPCDestroyed);
	}

}

void AABSection::OnKeyNPCDestroyed(AActor* DestoryedActor)
{
	auto ABCharacter = Cast<AABCharacter>(DestoryedActor);
	ABCHECK(nullptr != ABCharacter);

	auto ABPlayerController = Cast<AABPlayerController>(ABCharacter->LastHitBy);
	ABCHECK(nullptr != ABPlayerController);

	SetState(ESectionState::COMPLETE);
}

* NPC를 제거해 섹션을 클리어하면 게임모드에게 스코어를 올리라는 명령을 내린다
* 여기서 마지막 피격을 가한 플레이어 컨트롤러 정보를 함꼐 념겨서 해당 플레이어 스테이트의 스코어를 높이고, 더불어 전체 스코어에 해당하는 게임 스테이트의 스코어도 같이 올린다.

* 현재 게임에 참여 중인 플레이어 컨트롤러의 목록은 월드에서 제공하는 GetPlayerControllerIterator를 사용해 얻어올 수 있다. 이를 사용해 게임 스코어를 구현한 코드는 다음과 같다.  

//AABPlayerState.h
{
public:
    void AddGameScore();
}

//cpp
void AABPlayerState::AddGameScore()
{
	GameScore++;
	OnPlayerStateChanged.Broadcast();
}

//ABPlayerController.h
class ARENABATTLE_API AABPlayerController : public APlayerController
{
public:
...
	void AddGameScore() const;
}

//ABPlayerController.cpp
void AABPlayerController::AddGameScore() const
{
	ABPlayerState->AddGameScore();
}

//ABGameMode.h

UCLASS()
class ARENABATTLE_API AABGameMode : public AGameModeBase
{
	GENERATED_BODY()
public:
	AABGameMode();

	virtual void PostInitializeComponents() override;
	virtual void PostLogin(APlayerController* NewPlayer) override;
	void AddScore(class AABPlayerController * ScoredPlayer);

private:
	UPROPERTY()
	class AABGameState* ABGameState;
};

//cpp
#include "ABGameMode.h"
#include "ABCharacter.h"
#include "ABPlayerController.h"
#include "ABPlayerState.h"
#include "ABGameState.h"


AABGameMode::AABGameMode()
{
	DefaultPawnClass = AABCharacter::StaticClass();
	PlayerControllerClass = AABPlayerController::StaticClass();
	PlayerStateClass = AABPlayerState::StaticClass();
	GameStateClass = AABGameState::StaticClass();
}

void AABGameMode::PostLogin(APlayerController* NewPlayer)
{
	Super::PostLogin(NewPlayer);

	auto ABPlayerState = Cast<AABPlayerState>(NewPlayer->PlayerState);
	ABCHECK(nullptr != ABPlayerState);
	ABPlayerState->InitPlayerData();
}

void AABGameMode::PostInitializeComponents()
{
	Super::PostInitializeComponents();
	ABGameState = Cast<AABGameState>(GameState);
}

void AABGameMode::AddScore(class AABPlayerController * ScoredPlayer)
{
	for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
	{
		const auto ABPlayerController = Cast<AABPlayerController>(It->Get());
		if ((nullptr != ABPlayerController) && (ScoredPlayer == ABPlayerController))
		{
			ABPlayerController->AddGameScore();
			break;
		}
	}

	ABGameState->AddGameScore();
}

//ABSection.cpp
#include "ABGameMode.h"

void AABSection::OnKeyNPCDestroyed(AActor* DestoryedActor)
{
.......

	auto ABGameMode = Cast<AABGameMode>(GetWorld()->GetAuthGameMode());
	ABCHECK(nullptr != ABGameMode);
	ABGameMode->AddScore(ABPlayerController);

	SetState(ESectionState::COMPLETE);
}

 

* 컴파일 하고 실행결과 NPC가 제거되면 우측 상단의 스코어가 하나씩 올라간다.


+ Recent posts