Game State와 Game Mode

2025. 2. 4. 20:36·내배캠/Unreal Engine
목차
  1. 📌 GameState
  2. 📌 GameMode
  3. 📌 GameInstance
  4. 📌 GameState.h
  5. 📌 GameState.cpp
  6. ✅ GameState 동작 흐름
  7. ✅ Coin 클래스에서 호출하는 함수
  8. 📌 사용했던 언리얼 주요 함수 정리
  9. 🔎 UGameplayStatics 클래스 관련 메서드
  10. 🔎AActor 관련 메서드

 

 

언리얼 엔진에서 게임의 흐름과 데이터 관리를 위한 전역적 상태를 다루는 클래스에는 보통 Game State와 Game Mode 두 가지로 나뉘게 된다.

아래에서 GameState와 GameMode 그리고 게임의 레벨 간 데이터를 유지하는데 사용되는 GameInstance까지 정리하려한다.
그리고 싱글 플레이에 적합한 GameState를 예시로 추가적인 정리를 하겠다.

 

📌 GameState

GameState는 현재 게임의 전역 상태를 관리하는 클래스다. 클라이언트와 서버 간에 동기화되는 상태 정보를 담고 있다.

레벨이 전환될 때마다 새로 GameState가 생기기 때문에, 레벨이 전환되더라도 유지되어야 하는 데이터는 GameInstance를 사용한다.

📍 GameState에서 주로 관리하는 데이터

  • 점수, 플레이어 레벨, 남은 시간, 게임 종료 여부 등의 정보
  • 모든 플레이어에게 공유되어야 하는 전역 상태

🟢 GameState를 사용하는 이유

  • 게임 상태(점수, 시간 등)를 모든 클라이언트가 동일하게 참조 가능
  • 싱글플레이에서도 게임의 흐름을 관리하는 데 유용

📌 GameMode

GameMode는 게임 규칙과 로직을 정의하는 서버 전용 클래스다.
게임의 흐름과 승패 조건, 규칙을 제어하는 핵심 역할을 하며, 주로 서버에서 실행된다.

🔴 클라이언트가 GameMode에 접근할 수 없는 이유

  • GameMode는 서버에서만 동작하기 때문에 클라이언트에서 접근하려 하면 값이 동기화되지 않아 복잡한 문제가 생길 수 있다.
  • 따라서, 클라이언트도 알아야 할 정보는 GameState에 저장하는 것이 일반적이다.

📌 GameInstance

GameInstance는 게임의 전역 싱글톤 객체로, 레벨 간 데이터를 유지하는 데 사용된다.
레벨 전환 시에도 삭제되지 않기 때문에 전체 게임 데이터를 저장하는 데 적합하다.

📍 GameInstance에서 주로 관리하는 데이터

  • 총 점수, 진행 중인 레벨 정보, 설정 데이터 등
  • 한 번 초기화되면 게임이 종료될 때까지 유지된다.

🟢 싱글플레이어에서 유용한 이유

  • 한 레벨에서 다른 레벨로 넘어가더라도 데이터를 유지
  • 중간 저장 및 글로벌 데이터 관리에 적합

 


 

📌 GameState.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameState.h"
#include "SZ_GameState.generated.h"

UCLASS()
class STRIKEZONE_API ASZ_GameState : public AGameState
{
	GENERATED_BODY()
	
public:
	ASZ_GameState();

	virtual void BeginPlay() override;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Score")
	int32 Score;
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Coin")
	int32 SpawnedCoinCount;
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Coin")
	int32 CollectedCoinCount;
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Level")
	float LevelDuration;
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Level")
	int32 CurrentLevelIndex;
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Level")
	int32 MaxLevels;
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Level")
	TArray<FName> LevelMapNames;

	FTimerHandle LevelTimerHandle;

	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 OnLevelTimeUp();
	void OnCoinCollected();
	void EndLevel();
};

 

 

📌 GameState.cpp

#include "SZ_GameState.h"
#include "Kismet/GameplayStatics.h"
#include "ItemSpawnVolume.h"
#include "CoinItem.h"
#include "SZ_GameInstance.h"

ASZ_GameState::ASZ_GameState()
{
	Score = 0;
	SpawnedCoinCount = 0;
	CollectedCoinCount = 0;
	LevelDuration = 30.0f;
	CurrentLevelIndex = 0;
	MaxLevels = 3;
}

void ASZ_GameState::BeginPlay()
{
	Super::BeginPlay();

	StartLevel();
}

void ASZ_GameState::AddScore(int32 Amount)
{
	if (UGameInstance* GameInstance = GetGameInstance())
	{
		USZ_GameInstance* SZ_GameInstance = Cast<USZ_GameInstance>(GameInstance);
		if (SZ_GameInstance)
		{
			SZ_GameInstance->AddToScore(Amount);
		}
	}
}

void ASZ_GameState::StartLevel()
{
	if (UGameInstance* GameInstance = GetGameInstance())
	{
		USZ_GameInstance* SZ_GameInstance = Cast<USZ_GameInstance>(GameInstance);
		if (SZ_GameInstance)
		{
			CurrentLevelIndex = SZ_GameInstance->CurrentLevelIndex;
		}
	}
	SpawnedCoinCount = 0;
	CollectedCoinCount = 0;

	TArray<AActor*> FoundVolumes;
	UGameplayStatics::GetAllActorsOfClass(GetWorld(), AItemSpawnVolume::StaticClass(), FoundVolumes);

	const int32 ItemToSpawn = 40;
	for (int32 i = 0; i < ItemToSpawn; ++i)
	{
		if (FoundVolumes.Num() > 0)
		{
			AItemSpawnVolume* SpawnVolume = Cast<AItemSpawnVolume>(FoundVolumes[0]);
			if (SpawnVolume)
			{
				AActor* SpawnedActor = SpawnVolume->SpawnRandomItem();
				if (SpawnedActor && SpawnedActor->IsA(ACoinItem::StaticClass()))
				{
					SpawnedCoinCount++;
				}

			}
		}
	}

	GetWorldTimerManager().SetTimer
	(
		LevelTimerHandle,
		this,
		&ASZ_GameState::OnLevelTimeUp,
		LevelDuration,
		false
	);
}

void ASZ_GameState::OnLevelTimeUp()
{
	EndLevel();
}

void ASZ_GameState::OnCoinCollected()
{
	CollectedCoinCount++;
	
	if (SpawnedCoinCount > 0 && CollectedCoinCount >= SpawnedCoinCount)
	{
		EndLevel();
	}
}

void ASZ_GameState::EndLevel()
{
	GetWorldTimerManager().ClearTimer(LevelTimerHandle);

	if (UGameInstance* GameInstance = GetGameInstance())
	{
		USZ_GameInstance* SZ_GameInstance = Cast<USZ_GameInstance>(GameInstance);
		if (SZ_GameInstance)
		{
			AddScore(Score);
			CurrentLevelIndex++;
			SZ_GameInstance->CurrentLevelIndex = CurrentLevelIndex;
		}
	}

	if (CurrentLevelIndex >= MaxLevels)
	{
		OnGameOver();
		return;
	}
	
	if (LevelMapNames.IsValidIndex(CurrentLevelIndex))
	{
		UGameplayStatics::OpenLevel(GetWorld(), LevelMapNames[CurrentLevelIndex]);
	}
	else
	{
		OnGameOver();
	}
}

void ASZ_GameState::OnGameOver()
{
	// 게임 종료 로직
}

 

✅ GameState 동작 흐름

레벨이 전환되면 GameState가 새롭게 생성되며 BeginPlay부터 다시 실행하게 된다.
  1. BeginPlay( ) → StartLevel( ) 호출
  2. StartLevel( ) 동작
    • GameInstance에서 현재 레벨 정보를 가져와 GameState의 CurrentLevelIndex에 저장
    • 월드에 스폰되어있는 SpawnVolume을 가져와, SpawnRandomItem( ) 호출
    • 스폰된 액터가 코인일 경우 코인 개수 증가
    • LevelDuration 제한시간이 지나면 OnLevelTimeUp( ) 호출
  3. OnLevelTimeUp( ) → EndLevel( ) 호출
  4. EndLevel( ) 동작
    • 타이머 초기화
    • AddScore( ) 호출
      • GameInstance가 관리하는 TotalScore에 획득한 점수 합산
    • 현재 레벨을 증가시키고, 이를 GameInstance에도 반영
    • 현재 레벨이 마지막 레벨이면 OnGameOver( ) 호출하여 게임 종료
    • 마지막 레벨이 아니라면 UGameplayStatics::OpenLevel( ) 호출하여 다음 레벨로 전환
  5. OnGameOver( ) 동작
    • 게임 종료 로직

 

✅ Coin 클래스에서 호출하는 함수

AddScore( ): Coin 클래스에서 GameInstance에서 관리하는 TotalScore에 획득한 점수 합산 로직 호출
OnCoinCollected( ): Coin 클래스에서 코인 획득 개수 증가 및 레벨에 있는 코인 모두 획득시 레벨 종료 로직 호출
// 코인에 오버랩된 객체가 Player일 때 호출되는 함수
void ACoinItem::ActivateItem(AActor* Activator)
{
	if (Activator && Activator->ActorHasTag("Player"))
	{
		if (UWorld* World = GetWorld())
		{
			if (ASZ_GameState* GameState = World->GetGameState<ASZ_GameState>())
			{
				GameState->AddScore(PointValue);
				GameState->OnCoinCollected();
			}
		}
		DestroyItem();
	}
}

 


 

📌 사용했던 언리얼 주요 함수 정리

 

🔎 UGameplayStatics 클래스 관련 메서드

UGameplayStatics는 게임 실행 중 유용하게 사용할 수 있는 정적 유틸리티 함수들을 제공한다.

 

📍 UGameplayStatics::GetAllActorsOfClass

static void GetAllActorsOfClass(const UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, TArray<AActor*>& OutActors);

 

🟢 주요 특징

  • 주어진 특정 클래스 타입의 모든 액터를 월드에서 가져와 배열에 저장한다.
  • 월드에 존재하는 모든 액터를 대상으로 검색하므로 성능에 주의해야 한다.

 

📍 UGameplayStatics::OpenLevel

static bool OpenLevel(const UObject* WorldContextObject, FName LevelName, bool bAbsolute = true, FString Options = FString());

🟢 주요 특징

  • 주어진 레벨 이름에 따라 레벨 전환
  • 옵션을 설정해 추가적인 파라미터 전달 가능

 

🔎AActor 관련 메서드

📍 AActor::Is A

bool IsA(UClass* Class) const;

🟢 주요 특징

  • 동적 캐스팅 없이 클래스 확인 가능
  • 특정 오브젝트 타입에만 로직을 적용할 때 유용

'내배캠 > Unreal Engine' 카테고리의 다른 글

게임 메뉴 UI 디자인 / 게임 흐름에 맞게 UI 전환  (0) 2025.02.06
UI 위젯 설계와 실시간 데이터 연동  (0) 2025.02.06
캐릭터 데미지 적용  (0) 2025.02.04
가비지 컬렉션과 메모리 관리  (0) 2025.01.31
Pawn에 Possess 하기  (0) 2025.01.31
  1. 📌 GameState
  2. 📌 GameMode
  3. 📌 GameInstance
  4. 📌 GameState.h
  5. 📌 GameState.cpp
  6. ✅ GameState 동작 흐름
  7. ✅ Coin 클래스에서 호출하는 함수
  8. 📌 사용했던 언리얼 주요 함수 정리
  9. 🔎 UGameplayStatics 클래스 관련 메서드
  10. 🔎AActor 관련 메서드
'내배캠/Unreal Engine' 카테고리의 다른 글
  • 게임 메뉴 UI 디자인 / 게임 흐름에 맞게 UI 전환
  • UI 위젯 설계와 실시간 데이터 연동
  • 캐릭터 데미지 적용
  • 가비지 컬렉션과 메모리 관리
동그래님
동그래님
  • 동그래님
    개발자 동그래
    동그래님
  • 전체
    오늘
    어제
    • 분류 전체보기 (210)
      • 공부 (51)
        • Code Cata (50)
      • 내배캠 (151)
        • TIL (50)
        • C++ (37)
        • Unreal Engine (48)
        • GAS(Gameplay Ability System.. (16)
      • Project (7)
        • Gunfire Paragon (5)
        • Arena Fighters (1)
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.3
    동그래님
    Game State와 Game Mode
    상단으로

    티스토리툴바

    단축키

    내 블로그

    내 블로그 - 관리자 홈 전환
    Q
    Q
    새 글 쓰기
    W
    W

    블로그 게시글

    글 수정 (권한 있는 경우)
    E
    E
    댓글 영역으로 이동
    C
    C

    모든 영역

    이 페이지의 URL 복사
    S
    S
    맨 위로 이동
    T
    T
    티스토리 홈 이동
    H
    H
    단축키 안내
    Shift + /
    ⇧ + /

    * 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.