게임 루프와 주요 클래스 구조
게임 루프 안에서 GameInstance·GameMode·Controller·Pawn·컴포넌트가 맡는 역할과 호출 흐름을 정리합니다.
이전 절에서는 C++ 클래스를 생성하고 블루프린트로 확장하는 흐름을 확인했습니다.
이번 절에서는 액터와 클래스가 매 프레임 어떤 순서로 업데이트되는지 이해하기 위해 언리얼 엔진의 게임 루프(Game Loop)와 이를 구성하는 주요 클래스들의 구조를 다룹니다.
게임 루프: 프레임 처리 흐름
모든 게임은 기본적으로 무한 반복되는 주기를 가지고 동작합니다.
이를 게임 루프라고 부릅니다.
언리얼 엔진도 마찬가지로, 정해진 순서에 따라 매 프레임(Frame)마다 일련의 작업들을 수행하며 게임 세계를 업데이트합니다.
이 루프는 플레이어의 입력 처리, AI 업데이트, 물리 시뮬레이션, 애니메이션, 렌더링 등 게임 내의 모든 변화를 제어합니다.
언리얼 엔진의 게임 루프는 다음과 같은 주요 단계로 이해할 수 있습니다.
실제 엔진 내부 순서는 더 세분화되어 있지만, 여기서는 게임플레이 코드를 작성할 때 필요한 관점으로 단순화합니다.
입력 처리 (Input Processing): 키보드, 마우스, 게임패드 등 플레이어의 입력을 감지하고 처리합니다.
- 틱(Tick) 함수 호출: Tick이 활성화된 액터(
AActor)와 컴포넌트(UActorComponent)가 각자의 로직을 업데이트합니다. 액터는 주로Tick, 컴포넌트는TickComponent를 통해 이전 프레임으로부터 경과된 시간(DeltaTime)을 받아 동작을 수행합니다. - 물리 시뮬레이션: 액터들의 충돌 감지 및 반응, 중력 적용 등 물리 엔진이 게임 세계를 업데이트합니다.
- AI 업데이트: 인공지능 캐릭터들이 환경을 인지하고 다음 행동을 결정합니다.
렌더링 (Rendering): 업데이트된 게임 월드의 상태를 바탕으로 3D 장면을 화면에 그립니다.
카메라의 시점에 따라 모델, 텍스처, 조명, 셰이더 등을 계산하여 최종 이미지를 만듭니다.
사운드 업데이트 (Sound Update): 배경 음악, 효과음 등 게임 내의 사운드를 업데이트하고 재생합니다.
네트워크 동기화 (Networking Synchronization): 멀티플레이어 게임의 경우, 서버와 클라이언트 간의 게임 상태를 동기화합니다.
이 모든 과정이 매우 짧은 시간 안에 반복적으로 이루어지며, 이를 통해 우리가 보는 부드러운 게임 화면이 만들어집니다.
여러분이 AActor::Tick(float DeltaTime) 함수에 코드를 작성하면, 그 코드는 이 게임 로직 업데이트 단계에서 매 프레임마다 실행될 수 있습니다.
단, 필요한 액터나 컴포넌트에만 Tick을 켜는 편이 성능 관리에 유리합니다.
Tick은 편리하지만 매 프레임 비용을 약속하는 선택이므로, 이벤트나 타이머로 충분한 작업인지 먼저 나눠 보는 습관이 중요합니다.
게임 루프는 모든 프레임마다 돌아가지만, 모든 액터가 매번 계산할 필요는 없습니다. 입력, 이벤트, 타이머, 컴포넌트 책임을 먼저 나누면 프레임 예산을 지키기 쉽습니다.
- Tick업데이트 방식 선택
Tick 카메라 추적, 지속 이동, 보간처럼 화면마다 값이 바뀔 때만 켭니다. Event 키 입력, overlap, 상태 전환처럼 발생 시점이 분명하면 이벤트가 낫습니다. Timer 재생성, 회복, 주기 검사처럼 매 프레임이 필요 없는 작업에 맞습니다. Component 이동, 체력, 감지처럼 액터마다 붙는 기능은 컴포넌트로 나눕니다.
- Tick매 프레임 추적
Tick 카메라 추적, 지속 이동, 보간처럼 화면마다 값이 바뀔 때만 켭니다.
- Event입력과 충돌 반응
Event 키 입력, overlap, 상태 전환처럼 발생 시점이 분명하면 이벤트가 낫습니다.
- Timer간격 실행
Timer 재생성, 회복, 주기 검사처럼 매 프레임이 필요 없는 작업에 맞습니다.
- Component기능 격리
Component 이동, 체력, 감지처럼 액터마다 붙는 기능은 컴포넌트로 나눕니다.
- Tick을 켰다면 함께 확인할 것
DeltaTime 사용 프레임 속도와 무관한 이동량 계산 초당 속도에 DeltaTime 을 곱해 보정 활성 조건 필요한 동안만 tick 활성화 대기 상태 액터는 계산을 멈추기 책임 위치 Actor Tick과 Component Tick 중 하나 선택 기능 단위가 분명하면 컴포넌트에 배치
주요 클래스 구조와 역할
어디에 코드를 둘지 헷갈리면 클래스 이름보다 수명과 복제 범위를 먼저 본다.
| 클래스 | 맡는 책임 | 헷갈릴 때 기준 |
|---|---|---|
| GameInstance | 맵 전환 뒤에도 유지되는 전역 데이터 | 세션과 설정처럼 오래 산다 |
| GameMode | 서버 규칙, 스폰, 승패 조건 | 클라이언트가 직접 보지 않아도 된다 |
| GameState | 점수, 라운드, 남은 시간 같은 공유 상태 | 모든 클라이언트가 봐야 한다 |
| Controller / Pawn | 입력과 의도 전달 / 실제 이동과 충돌 | 명령을 내리는 쪽과 움직이는 쪽을 분리한다 |
언리얼 엔진의 게임 루프와 밀접하게 연관되어 게임의 전반적인 흐름을 제어하는 몇 가지 핵심 클래스들이 있습니다.
이 클래스들은 특정 역할을 전담하며, 서로 유기적으로 협력하여 하나의 게임을 완성합니다.
UGameInstance
- 역할:
UGameInstance는 게임 전체의 수명 주기 동안 유지되는 상위 레벨 객체입니다. 일반적인 실행 컨텍스트에서는 게임이 시작될 때 생성되어 게임이 종료될 때까지 유지됩니다. - 특징: 레벨이 변경되어도 파괴되지 않기 때문에, 여러 레벨에 걸쳐 유지되어야 하는 데이터(예: 플레이어 진행 상황, 설정, 영구적인 네트워크 연결 등)를 저장하고 관리하는 데 이상적입니다.
- 활용: 게임 전반에 걸친 데이터 관리, 세션 관리, 로딩 화면 전환, 설정 저장/로드 등에 사용됩니다.
AGameModeBase / AGameMode
- 역할:
AGameModeBase(또는 더 구체적인 게임 유형에 맞는AGameMode)는 게임의 규칙과 흐름을 정의하는 클래스입니다. 예를 들어, 어떤 캐릭터 클래스를 사용할지, 플레이어의 스폰 지점은 어디인지, 점수 계산 방식은 무엇인지, 게임의 승리/패배 조건은 무엇인지 등을 설정합니다. - 특징:
AGameMode는 서버에서만 존재하며, 클라이언트에는 복제되지 않습니다. 이는 게임 규칙이 서버에서 단일하게 관리되어야 함을 의미합니다. 클라이언트에서는AGameStateBase(후술)가 게임 상태 정보를 공유받습니다. - 활용: 게임 모드 변경, 플레이어 접속/종료 처리, 게임 시작/종료 로직, 기본 Pawn/Controller/HUD 클래스 지정 등에 사용됩니다. 실제 UI 표시와 입력 처리는 보통 클라이언트의 PlayerController, HUD, Widget 쪽에서 담당합니다.
AGameStateBase / AGameState
- 역할:
AGameStateBase(또는AGameState)는 게임의 현재 상태 정보를 모든 클라이언트와 공유하는 역할을 합니다. 즉, 게임의 규칙(AGameMode)에 따라 변동되는 플레이어 점수, 남은 시간, 현재 라운드, 참여 플레이어 목록 등과 같은 정보를 담고, 이를 모든 플레이어에게 전달합니다. - 특징:
AGameState는 서버와 모든 클라이언트에 복제되어 존재합니다. 이를 통해 클라이언트는 서버의 게임 상태를 파악하고 그에 맞는 UI를 표시하거나 동작을 수행할 수 있습니다. - 활용: 점수판 업데이트, 라운드 정보 표시, 게임 진행 상태 표시 등에 사용됩니다.
APlayerController
- 역할:
APlayerController는 플레이어의 입력을 처리하고, 해당 플레이어가 조종하는APawn또는ACharacter를 제어하는 클래스입니다. 키보드, 마우스, 게임패드 등의 입력 이벤트를 받고, 이를 게임 로직으로 변환하여 캐릭터를 움직이거나 상호작용하게 만듭니다. - 특징: 각 플레이어마다 하나씩 존재하며, 서버와 해당 플레이어의 클라이언트에 존재합니다. 다른 플레이어의 클라이언트에는 보통 해당 PlayerController가 직접 존재하지 않습니다. AI가 제어하는 캐릭터는
AAIController가 담당합니다. - 활용: 캐릭터 이동, 카메라 제어, UI 상호작용, 인벤토리 관리, 무기 발사 등 플레이어와 관련된 거의 모든 입력 및 상호작용 로직에 관여합니다.
APawn / ACharacter
- 역할:
APawn은APlayerController나AAIController에 의해 빙의(Possess)되어 제어될 수 있는 액터의 기본 클래스입니다. 즉, 플레이어나 AI가 월드 내에서 조종할 수 있는 실제 객체를 나타냅니다.ACharacter는APawn을 상속받으며, 사람 형태의 이동과 관련된 복잡한 로직(걷기, 뛰기, 점프, 웅크리기 등)을 구현하는 데 특화되어 있습니다. - 특징: 폰은 월드에 배치되며, 변환 정보를 가집니다.
ACharacter는 자체적으로UCharacterMovementComponent를 포함하여 복잡한 이동을 쉽게 처리할 수 있습니다. - 활용: 플레이어 캐릭터, 적 AI, 차량, 비행기 등 게임 내에서 플레이어 또는 AI에 의해 움직이거나 상호작용할 수 있는 모든 대상을 만들 때 사용됩니다.
UActorComponent / USceneComponent
- 역할:
UActorComponent는 액터가 소유하고 등록하는 기능 모듈이며,USceneComponent는UActorComponent를 상속받아 3D 공간의 위치, 회전, 스케일과 부착(Attachment) 계층을 가지는 컴포넌트입니다. - 특징: 액터 하나에 여러 컴포넌트를 조립해 복잡한 기능을 만들 수 있습니다. 예를 들어,
UStaticMeshComponent(3D 모델),UCameraComponent(카메라 시점),UParticleSystemComponent(파티클 효과),UAudioComponent(사운드) 등이 있습니다. - 활용: 액터의 시각적 표현, 충돌 감지, 물리 시뮬레이션, 카메라, 사운드, 커스텀 로직 등 특정 모듈화된 기능을 구현할 때 사용됩니다.
이 역할 분리를 실제 설계에 적용할 때는 수명, 권한, 복제 범위를 먼저 묻는 것이 좋습니다.
익숙한 클래스에 넣는 것이 아니라 데이터가 얼마나 오래 살고 누가 수정할 권한을 갖는지로 정한다.
| 질문 | 예라면 둘 곳 | 아니면 볼 곳 |
|---|---|---|
| 레벨 밖에서도 유지되는가 | GameInstance | 레벨별 Actor나 GameState |
| 서버만 결정해야 하는가 | GameMode | 공유 결과는 GameState |
| 클라이언트 UI가 봐야 하는가 | GameState 또는 PlayerState | 서버 전용 규칙은 GameMode |
| 입력인지 월드 객체인지 | PlayerController / Pawn 또는 Component | 전역 규칙과 분리한다 |
클래스 간의 관계도 (간략화)
클래스 이름부터 고르지 말고 데이터의 수명과 공개 범위를 먼저 묻는다. 오래 사는 객체일수록 더 적고 안정적인 정보만 가져야 한다.
- UGameInstance
APP LIFETIME 레벨 전환 뒤에도 남아야 하는 세션, 설정, 진행도만 둔다. 현재 월드의 액터 참조는 오래 보관하지 않는다.
- APP LIFETIME
- GameMode → GameState
SERVER → ALL 서버 전용 GameMode가 규칙과 승패를 판정하고, 클라이언트가 읽을 점수·시간·라운드는 GameState로 복제한다.
- SERVER → ALL
- APlayerController
PLAYER LIFETIME 해당 플레이어의 입력, 카메라, 로컬 UI 명령을 소유하고 현재 Pawn을 제어한다. 월드의 몸과 판단 주체를 분리한다.
- PLAYER LIFETIME
- Pawn · Actor · Component
SPAWN LIFETIME 스폰부터 파괴까지 존재하는 몸체와 이동·충돌·감지 같은 기능을 둔다. 재사용 기능은 작은 Component로 조립한다.
- SPAWN LIFETIME
이러한 클래스들의 구조와 역할을 이해하는 것은 언리얼 엔진에서 C++ 코드를 작성할 때 무엇을 어디에 구현해야 하는가에 대한 명확한 가이드라인을 제공합니다.
이제는 단순히 코드를 작성하는 것을 넘어, 언리얼 엔진이 어떻게 동작하는지에 대한 큰 그림을 그릴 수 있게 되었습니다.
게임 루프 안에서 각 클래스가 언제 데이터를 만들고, 누가 읽고, 어느 시점에 정리하는지 함께 보면 코드 배치 기준이 더 분명해집니다.
Actor, Pawn, Controller, Component는 역할 이름보다 언제 생성되고 누가 읽는지로 배치하면 기준이 분명해집니다.
- World 준비
GameMode가 기본 클래스와 규칙을 정합니다.
- Possess
Controller가 Pawn 또는 Character를 제어합니다.
- BeginPlay
Actor와 Component가 참조와 초기 상태를 준비합니다.
- Tick
프레임마다 필요한 이동, 감지, 갱신만 수행합니다.
- Read
UI와 다른 시스템이 상태를 읽고 표시합니다.
- EndPlay
등록한 참조, 타이머, 이벤트를 정리합니다.
게임 루프와 주요 클래스 구조는 프레임 단위 실행 흐름과 책임 배치를 이해하는 기준입니다.
다음 장에서는 APawn, ACharacter, AController를 중심으로 게임플레이 클래스의 역할 분리와 구현 기준을 다룹니다.
Tick 비용과 프레임워크 클래스의 데이터 소유 위치를 함께 보며 코드 배치 기준을 마무리합니다.
Tick 한 줄보다 어떤 객체가 언제 생성되고 어떤 책임을 맡는지가 더 중요하다.
| 흐름 | 책임 객체 | 확인할 것 |
|---|---|---|
| Init | GameInstance | 게임 실행 전체에 살아 있는 데이터와 세션 흐름 |
| Spawn Rule | GameMode | 기본 Pawn, 승패 조건, 서버 규칙 |
| Possess | Controller | 입력과 의사결정을 Pawn에 전달하는 소유 관계 |
| Runtime Body | Pawn / Character | 월드에서 움직이고 충돌하는 실행 대상 |