C++와 Unreal Engine으로 3D 게임 개발 3-4,5

1. WHAT
- GameState는 레벨 단위의 게임 루프(시간 제한, 코인 수, 레벨 진행)를 관리하는 전역 상태 관리자이다.
- GameMode는 규칙만 담당하고, 클라이언트도 알아야 하는 정보는 GameState에 둔다.
- GameInstance는 레벨 전환 후에도 유지돼야 하는 누적 데이터(점수 등)를 저장한다.
- 캐릭터 체력 시스템 구현
- 싱글 플레이 기준
- 체력은 플레이어 한 명만 관리하면 되므로 Character 클래스에서 직접 관리
- 언리얼 기본 데미지 시스템(ApplyDamage → TakeDamage) 활용
🧱 체력 관리 위치: Character 클래스
Character에 포함되는 요소
- MaxHealth : 최대 체력
- Health : 현재 체력
- TakeDamage() : 데미지 처리
- AddHealth() : 회복 처리
- OnDeath() : 사망 처리
⚔️ 데미지 처리 흐름
ApplyDamage → TakeDamage 구조
- UGameplayStatics::ApplyDamage
- 외부에서 “데미지를 준다”는 행위
- 대상 액터의 TakeDamage() 호출
- AActor::TakeDamage
- 실제 체력 감소 로직 처리
- 캐릭터 클래스에서 오버라이드
void AMineItem::Explode()
{
TArray<AActor*> OverlappingActors;
ExplosionCollision->GetOverlappingActors(OverlappingActors);
for (AActor* Actor : OverlappingActors)
{
if (Actor && Actor->ActorHasTag("Player"))
{
// 데미지를 발생시켜 Actor->TakeDamage()가 실행되도록 함
UGameplayStatics::ApplyDamage(
Actor, // 데미지를 받을 액터
ExplosionDamage, // 데미지 양
nullptr, // 데미지를 유발한 주체 (지뢰를 설치한 캐릭터가 없으므로 nullptr)
this, // 데미지를 유발한 오브젝트(지뢰)
UDamageType::StaticClass() // 기본 데미지 유형
);
}
}
// 폭발 이후 지뢰 아이템 파괴
DestroyItem();
}
float ASpartaCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
// 기본 데미지 처리 로직 호출 (필수는 아님)
float ActualDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
// 체력을 데미지만큼 감소시키고, 0 이하로 떨어지지 않도록 Clamp
Health = FMath::Clamp(Health - DamageAmount, 0.0f, MaxHealth);
UE_LOG(LogTemp, Warning, TEXT("Health decreased to: %f"), Health);
// 체력이 0 이하가 되면 사망 처리
if (Health <= 0.0f)
{
OnDeath();
}
// 실제 적용된 데미지를 반환
return ActualDamage;
}
TakeDamage 핵심 개념
- 데미지 값, 가해자, 데미지 원인 등을 함께 전달
- 반환값은 실제 적용된 데미지
- 방어력, 무적 상태 같은 확장 로직에 활용 가능
❤️ 회복 처리 (Healing)
- AddHealth(float Amount)
- FMath::Clamp 사용
- 체력이 0 ~ MaxHealth 범위를 넘지 않도록 제한
- 힐 아이템은 직접 AddHealth 호출
☠️ 사망 처리
- Health <= 0일 때 OnDeath() 호출
- 일반적인 처리 예:
- 입력 비활성화
- 사망 애니메이션
- Ragdoll
- 액터 제거 or 리스폰 처리
- 점수 관리 시스템 구현
- 점수는 게임 전체에서 공유되는 전역 데이터
- 따라서 GameState에서 관리
- GameMode는 “규칙”, GameState는 “상태”
🎯 GameMode
- 게임 규칙 관리
- 스폰 방식, 승패 조건
- 서버 전용 (클라이언트 없음)
🌍 GameState
- 전역 상태 저장소
- 점수, 남은 시간, 현재 게임 단계
- 싱글/멀티 모두 사용 가능
- 멀티플레이 시 자동 동기화 지원
👉 전역적으로 공유되는 데이터는 GameState가 적합
📊 GameState에 점수 시스템 구현
포함 요소
- Score : 현재 점수
- AddScore(int32 Amount) : 점수 증가
- GetScore() : UI 등에서 점수 조회
장점
- 점수 관련 로직을 한 곳에서 관리
- UI, 아이템, 게임 로직 어디서든 접근 가능
- 추후 멀티플레이 확장에도 구조 유지 가능
🔗 GameMode ↔ GameState 연동
- GameStateBase를 상속한 커스텀 GameState 생성
- GameMode의 GameStateClass에 설정
- Project Settings → Maps & Modes
- Default GameMode 설정
- GameState Class 지정
👉 이후 GetWorld()->GetGameState<>()로 언제든 접근 가능
🪙 코인 아이템 점수 처리
- 코인 획득 시:
- GameState 가져오기
- AddScore(PointValue) 호출
- 코인 제거 (중복 획득 방지)
- 점수 증가 책임은 항상 GameState
- GameState를 이용한 게임 루프 구현
GameMode
- 게임 규칙 관리자
- 스폰 규칙, 승패 조건, 팀 설정
- 서버 전용
- 클라이언트는 접근 불가
👉 “결정하는 곳”
GameState
- 게임 전역 상태 관리자
- 점수, 남은 시간, 현재 레벨 진행 상황
- 서버에서 생성 → 클라이언트로 복제
- 모든 플레이어가 동일한 정보 공유
👉 “모두가 알아야 하는 상태 저장소”
🧱 관리 대상 데이터
- CurrentLevelIndex : 현재 레벨
- MaxLevels : 총 레벨 수
- SpawnedCoinCount : 생성된 코인 수
- CollectedCoinCount : 획득한 코인 수
🔁 게임 흐름 함수 구조
BeginPlay()
- 게임 시작
- StartLevel() 호출
StartLevel()
- 코인 카운트 초기화
- SpawnVolume에서 40개 아이템 스폰
- 코인이면 SpawnedCoinCount++
- 30초 타이머 시작
OnCoinCollected()
- 코인 획득 시 호출
- CollectedCoinCount++
- 모든 코인을 모았으면 즉시 EndLevel()
OnLevelTimeUp()
- 30초 타이머 종료
- EndLevel() 호출
EndLevel()
- 타이머 정리
- CurrentLevelIndex++
- 남은 레벨 확인
- 있으면 다음 레벨 시작
- 없으면 OnGameOver()
OnGameOver()
- 게임 종료 처리
- 로그 출력 또는 UI 호출
- 이후 재시작 로직 확장 가능
⚠️ 레벨 전환 시 주의점
- UGameplayStatics::OpenLevel
- 현재 월드 제거
- GameState 재생성
- 즉, 레벨 간 데이터는 유지되지 않음
- GameInstance를 활용한 데이터 유지
GameInstance 란
- 게임 실행 시 생성
- 게임 종료까지 단 하나만 존재
- 레벨 전환 시에도 파괴되지 않음
👉 “레벨을 넘어 유지해야 하는 데이터 저장소”
- 전체 게임 루프 한눈에 보기
- 게임 실행
- GameInstance 생성
- GameMode / GameState 생성
- 첫 레벨 로드
- 레벨 시작
- GameState BeginPlay() → StartLevel()
- 아이템 40개 스폰
- 30초 타이머 시작
- 코인 획득
- CoinItem → 점수 증가
- GameState → 코인 카운트 체크
- 레벨 종료 조건
- 시간 초과 또는
- 모든 코인 획득
- 레벨 전환
- OpenLevel()
- GameState 재생성
- GameInstance 데이터 유지
- 게임 종료
- GameOver 처리
- UI 또는 재시작 로직 연결
2. RESULT

GameMode, GameState, GameInstance로 분리하니 게임 흐름을 구조적으로 설계할 수 있었다.
그리고 상태와 데이터 관리의 중요성을 느꼈다.
'내배캠Unreal_TIL > UE' 카테고리의 다른 글
| [TIL] 2026-01-22 | Automata, State Machine (0) | 2026.01.22 |
|---|---|
| [TIL] 2026-01-22 | UE GameplayTags (0) | 2026.01.22 |
| [TIL] 2026-01-20 | 과제 6에 타이머로 자동문 추가 (2) | 2026.01.20 |
| [TIL] 2026-01-19 | UE 아이템 시스템 구현하기 (2), Timer, SpawnActor (0) | 2026.01.19 |
| [TIL] 2026-01-16 | UE 아이템 시스템 구현하기 (1), 아이템 인터페이스 (1) | 2026.01.16 |