티스토리 뷰
🟩 오늘의 목표
- GameState를 활용해 레벨별 시간 제한, 코인 수집 개수 확인 등 전역 게임 루프를 설계한다.
- SpawnVolume이 스폰된 액터를 반환하도록 수정하여 생성된 아이템의 타입을 추적한다.
- 레벨 전환 시에도 데이터가 유지될 수 있도록 Game Instance의 개념과 활용법을 익힌다.
🟧 1. GameState를 이용한 게임 루프 구현
🟦 GameState와 GameMode의 역할 분담
- GameMode : 서버 전용 로직과 게임의 규칙(팀 배정, 승패 조건 등)을 정의한다. 클라이언트는 접근할 수 없다.
- GameState : 모든 플레이어가 공유해야 하는 상태(남은 시간, 점수 등)를 관리한다. 서버에서 생성되어 클라이언트로 복제된다.
- 이번 프로젝트에서는 멀티플레이 확장성을 고려하여 클라이언트도 알아야 할 정보가 많으므로 GameState에 루프 로직을 구현한다.
🟦 SpawnVolume 스폰 데이터 반환 수정
스폰된 아이템이 코인인지 확인하여 카운팅하기 위해 SpawnRandomItem 함수의 리턴 타입을 AActor*로 변경한다.
// SpawnVolume.cpp 핵심 수정
AActor* ASpawnVolume::SpawnRandomItem()
{
if (FItemSpawnRow* SelectedRow = GetRandomItem())
{
if (UClass* ActualClass = SelectedRow->ItemClass.Get())
{
// 스폰된 액터 포인터를 리턴하여 외부에서 타입을 확인할 수 있게 함
return SpawnItem(ActualClass);
}
}
return nullptr;
}
AActor* ASpawnVolume::SpawnItem(TSubclassOf<AActor> ItemClass)
{
if (!ItemClass) return nullptr;
AActor* SpawnedActor = GetWorld()->SpawnActor<AActor>(
ItemClass,
GetRandomPointInVolume(),
FRotator::ZeroRotator
);
return SpawnedActor;
}
🟦 SpartaGameState를 통한 게임 루프 설계
레벨 시작 시 아이템을 스폰하고, 타이머를 설정하며, 코인 수집 현황에 따라 레벨을 종료하는 로직을 관리한다.
// SpartaGameState.cpp 주요 루프 로직
void ASpartaGameState::StartLevel()
{
SpawnedCoinCount = 0;
CollectedCoinCount = 0;
TArray<AActor*> FoundVolumes;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), ASpawnVolume::StaticClass(), FoundVolumes);
const int32 ItemToSpawn = 40;
for (int32 i = 0; i < ItemToSpawn; i++)
{
if (FoundVolumes.Num() > 0)
{
ASpawnVolume* SpawnVolume = Cast<ASpawnVolume>(FoundVolumes[0]);
if (SpawnVolume)
{
AActor* SpawnedActor = SpawnVolume->SpawnRandomItem();
// 스폰된 액터가 코인이면 카운트 증가
if (SpawnedActor && SpawnedActor->IsA(ACoinItem::StaticClass()))
{
SpawnedCoinCount++;
}
}
}
}
// 30초 제한 시간 타이머 설정
GetWorldTimerManager().SetTimer(LevelTimerHandle, this, &ASpartaGameState::OnLevelTimeUp, LevelDuration, false);
}
void ASpartaGameState::OnCoinCollected()
{
CollectedCoinCount++;
// 모든 코인을 주웠다면 즉시 레벨 종료
if (SpawnedCoinCount > 0 && CollectedCoinCount >= SpawnedCoinCount)
{
EndLevel();
}
}
🟧 2. Game Instance를 활용한 데이터 유지
🟦 레벨 전환과 데이터 유실 문제
UGameplayStatics::OpenLevel을 통해 맵을 이동하면 기존의 GameState와 GameMode는 파괴되고 새로 생성되어 모든 변수가 초기화된다.
🟦 USpartaGameInstance의 역할
- Game Instance : 게임 애플리케이션이 실행될 때 생성되어 종료될 때까지 유지되는 유일한 객체다.
- TotalScore나 CurrentLevelIndex처럼 맵이 바뀌어도 유지되어야 하는 전역 데이터를 저장하는 데 사용한다.
// SpartaGameInstance.h
UCLASS()
class SPARTAPROJECT_API USpartaGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
USpartaGameInstance();
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "GameData")
int32 TotalScore;
UFUNCTION(BlueprintCallable, Category = "GameData")
void AddToScore(int32 Amount);
};
🟧 핵심 요약 및 주의사항
- OpenLevel 주의사항 : 맵 전환 시 GameState의 데이터가 날아가므로, 누적 점수 등은 반드시 GameInstance에 따로 저장해야 한다.
- IsA 함수 활용 : 스폰된 액터가 특정 클래스(ACoinItem)인지 확인할 때 매우 유용하다.
- 타이머 관리 : 레벨이 강제 종료되거나 전환될 때는 ClearTimer를 통해 돌아가고 있는 타이머를 정리해줘야 버그를 방지할 수 있다.
- 코인 수집 알림 : CoinItem이 파괴되기 직전에 GameState->OnCoinCollected()를 호출하여 수집 상태를 갱신해야 한다.
'내일배움캠프 Unreal_7기 > 본캠프' 카테고리의 다른 글
| [CH 3] 팀프로젝트 - 쌍권총 연사 시스템 구현 (0) | 2026.02.09 |
|---|---|
| TIL - 47일 (0) | 2026.02.03 |
| TIL - 45일차 (0) | 2026.01.30 |
| TIL - 44일차 (0) | 2026.01.29 |
| TIL - 43일차 (1) | 2026.01.28 |

