GameState
와 GameMode
가 있음.GameMode
에 직접 접근할 수 없기 때문에 클라이언트도 알아야하는 정보를 GameMode
에 두면 복잡해짐.GameMode
에 두고,GameState
에 두는 방식을 많이 사용.GameState
: 게임의 전역 데이터를 관리GameState
객체를 게임이 시작될 때 서버에서 생성되고,AActor*
를 반환하도록 변경.GameMode
버전과의 일관성을 위해서 GameStateBase
를 상속했던 것을 GameState
를 상속하도록 변경.//.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameState.h"
#include "Cp2GameState.generated.h"
UCLASS()
class CHAPTER2_API ACp2GameState : public AGameState
{
GENERATED_BODY()
public:
ACp2GameState();
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Score")
int32 Score;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Coin")
int32 SpawnedCoinCount;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Coin")
int32 CollectedCoinCount;
virtual void BeginPlay() override;
UFUNCTION(BlueprintPure, Category = "Score")
int32 GetScore() const { return Score; }
UFUNCTION(BlueprintCallable, Category = "Score")
void AddScore(int32 amount);
UFUNCTION(BlueprintCallable, Category = "Level")
void OnGameOver();
void StartLevel();
void EndLevel();
void OnCoinCollected();
}
SpawnVolume
BP 액터를 찾아서 아이템을 생성해줄 것.// .cpp
~~
#include "Kismet/GameplayStatics.h"
~~
void ACp2GameState::StartLevel()
{
SpawnedCoinCount = 0;
CollectedCoinCount = 0;
// 레벨에 있는 스폰 볼륨 찾기
TArray<AActor*> FoundVolume;
// 현재 레벨에서 원하는 클래스를 찾아내는 함수
UGameplayStatics::GetAllActorsOfClass(GetWorld(), ASpawnVolume::StaticClass(), FoundVolume);
const int32 ItemToSpawn = 40;
for(int32 i = 0; i < ItemToSpawn; ++i)
{
if (FoundVolume.Num() > 0)
{
ASpawnVolume* SpawnVolume = Cast<ASpawnVolume>(FoundVolume[0]);
if(SpawnVolume)
{
AActor* SpawnedActor = SpawnVolume->SpawnRandomItem();
// 해당 클래스가 코인 아이템인지
if (SpawnedActor && SpawnedActor->IsA(ACoinItem::StaticClass()))
++SpawnedCoinCount;
}
}
}
}
~~
레벨 내 액터 찾기
// 현재 맵에 배치된 모든 SpawnVolume을 찾아 아이템 40개를 스폰
TArray<AActor*> FoundVolumes;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), ASpawnVolume::StaticClass(), FoundVolumes);
UGameplayStatics::GetAllActorsOfClass(...)
#include "Kismet/GameplayStatics.h"
포함 필요아이템 생성 후 코인 세기
// 해당 클래스가 코인 아이템인지
if (SpawnedActor && SpawnedActor->IsA(ACoinItem::StaticClass()))
++SpawnedCoinCount;
IsA(Type)
Type
클래스거나 상속받는 하위 클래스인지 확인 검사함.boolean
void ACp2GameState::OnCoinCollected()
{
++CollectedCoinCount;
if (SpawnedCoinCount > 0 && CollectedCoinCount >= SpawnedCoinCount)
EndLevel();
}
void ACp2GameState::EndLevel()
{
// 타이머 초기화
GetWorldTimerManager().ClearTimer(LevelTimerHandle);
// 변경 사항 저장
if (UGameInstance* gameInst = GetGameInstance())
{
UCp2GameInstance* cp2GameInst = Cast<UCp2GameInstance>(gameInst);
if (cp2GameInst)
{
AddScore(Score);
++CurrentLevelIndex;
cp2GameInst->CurrenctLevelIndex = CurrentLevelIndex;
}
}
if(CurrentLevelIndex >= MaxLevel)
{
OnGameOver();
return;
}
if (LevelNames.IsValidIndex(CurrentLevelIndex))
UGameplayStatics::OpenLevel(GetWorld(), LevelNames[CurrentLevelIndex]);
else
OnGameOver();
}
UGamePlayStatics::OpenLevel(...)
맵 전환. 레벨 이동 함수BeginPlay()
가 호출됨.GameState
도 새로 생성되니 이전 레벨에서 유지하던 변수가 모두 초기화됨.#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "Cp2GameInstance.generated.h"
UCLASS()
class CHAPTER2_API UCp2GameInstance : public UGameInstance
{
GENERATED_BODY()
public:
UCp2GameInstance();
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "GameData")
int32 TotalScore;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "GameData")
int32 CurrenctLevelIndex;
UFUNCTION(BlueprintCallable, Category = "GameData")
void AddToScore(int32 scoreToAdd);
};
GameState
에서 점수 획득 시 호출하는 함수 수정GameState
를 통해서 AddScore()
해줄 것.GameInstance
에 반영해줌.void ACp2GameState::AddScore(int32 amount)
{
if(UGameInstance* gameInst = GetGameInstance())
{
UCp2GameInstance* cp2GameInst = Cast<UCp2GameInstance>(gameInst);
if (cp2GameInst)
cp2GameInst->AddToScore(amount);
}
}
UE에선 타이머를 관리하는 기능을 지원하고 있음.
이번 프로젝트에서 레벨 당 30초의 타이머를 설정해서 제한시간을 줄 것.
GameState
에 타이머 관련 변수#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameState.h"
#include "Cp2GameState.generated.h"
UCLASS()
class CHAPTER2_API ACp2GameState : public AGameState
{
GENERATED_BODY()
public:
ACp2GameState();
~~
FTimerHandle LevelTimerHandle;
void OnLevelTimeUp();
~~
};
void ACp2GameState::StartLevel()
{
~~
// 타이머
GetWorldTimerManager().SetTimer(
LevelTimerHandle,
this,
&ACp2GameState::OnLevelTimeUp,
LevelDuration,
false
);
}
void ACp2GameState::OnLevelTimeUp()
{
EndLevel();
}
GetWorldTimerManager().SetTimer(...)
: 타이머 관리자에서 타이머를 설정해줌.FTimerHandle& InOutHandle
: 설정해줄 타이머 구조체 참조GameState* InObj
: 현재 월드의 GameState
객체.GameState
에서 호출하므로 this
넣어줌void GameState::*InTimerMethod()
: 타이머가 완료되면 호출할 함수 포인터float InRate
: 타이머 설정 시간.bool InbLoop
: 반복 여부float InFirstDelay
: 타이머 딜레이를 주는 시간. (선택 옵션)