게임 루프와 주요 클래스 구조
게임 루프 안에서 GameInstance·GameMode·Controller·Pawn·컴포넌트가 맡는 역할과 호출 흐름을 정리합니다.
이전 절에서는 C++ 클래스를 생성하고 블루프린트로 확장하는 흐름을 확인했습니다.
이번 절에서는 액터와 클래스가 매 프레임 어떤 순서로 업데이트되는지 이해하기 위해 언리얼 엔진의 게임 루프(Game Loop)와 이를 구성하는 주요 클래스들의 구조를 다룹니다.
게임 루프: 프레임 처리 흐름
모든 게임은 기본적으로 무한 반복되는 주기를 가지고 동작합니다.
이를 게임 루프라고 부릅니다.
언리얼 엔진도 매 프레임(Frame) 여러 작업을 수행하며 게임 세계를 업데이트합니다. 작업의 실행 주기와 스레드, 선후 관계는 각 시스템의 설정에 따라 달라집니다.
이 루프는 플레이어의 입력 처리, AI 업데이트, 물리 시뮬레이션, 애니메이션, 렌더링 등 게임 내의 모든 변화를 제어합니다.
프레임 처리에 참여하는 주요 작업은 다음과 같습니다. 이 목록은 전체 엔진의 고정된 실행 순서를 뜻하지 않습니다.
- 입력 처리: 키보드·마우스·게임패드 입력을 게임 동작으로 연결합니다.
- 게임 로직: 활성화된 액터의 Tick과 컴포넌트의 TickComponent가 지정된 주기로 실행됩니다. DeltaTime은 해당 틱에 전달된 경과 시간입니다.
- 물리와 AI: 충돌·중력 등의 시뮬레이션과 환경 인식·행동 선택을 갱신합니다.
- 렌더링과 사운드: 카메라·메시·텍스처·조명으로 화면을 만들고 음악과 효과음을 처리합니다.
- 네트워크: 멀티플레이에서 필요한 상태를 정해진 복제 조건과 주기에 따라 교환합니다.
이 모든 과정이 매우 짧은 시간 안에 반복적으로 이루어지며, 이를 통해 우리가 보는 부드러운 게임 화면이 만들어집니다.
여러분이 AActor::Tick(float DeltaTime) 함수에 코드를 작성하면, 그 코드는 이 게임 로직 업데이트 단계에서 매 프레임마다 실행될 수 있습니다.
단, 필요한 액터나 컴포넌트에만 Tick을 켜는 편이 성능 관리에 유리합니다.
Tick의 실행 간격, Tick Group, 선행 틱 의존성을 설정할 수 있습니다. 매 프레임 처리가 필요한지부터 판단합니다.
예시: 프레임 수 대신 경과 시간을 계산하기
물리·충돌·감쇠 없이 한 축을 따라 100 cm/s로 이동하는 계산을 생각해 봅시다. 한 틱의 이동량은 100 × DeltaTime cm입니다. 30번의 틱이 각각 약 1/30초이거나, 60번의 틱이 각각 약 1/60초여도 전달된 경과 시간의 합이 1초라면 총 이동량은 둘 다 100 × 1 = 100cm입니다. 매 틱에 100 cm를 더하면 각각 3,000 cm와 6,000 cm가 되어 틱 횟수에 따라 결과가 달라집니다. 이 비교는 단순한 일정 속도 적분이며, 실제 물리 이동이나 시간 배율·일시 정지 조건을 자동으로 해결하는 코드는 아닙니다.
Actor Ticking에서 틱 간격과 그룹·선행 의존성을 확인할 수 있습니다.
UNREAL C++ · CH2-4
언제 갱신해야 하는가
연속 계산은 Tick, 사건에 대한 반응은 이벤트, 일정 간격이나 지연 작업은 타이머를 먼저 검토합니다.
| 방식 | 알맞은 작업 | 주의할 경계 |
|---|---|---|
| Tick | 연속 이동·보간처럼 주기적으로 갱신할 계산 | 활성화·실행 간격·Tick Group·선행 의존성 확인 |
| 이벤트 | 입력·충돌·상태 변경에 대한 반응 | 사건이 발생하고 연결된 대상이 있을 때 호출 |
| 타이머 | 일정 간격의 갱신이나 지연 작업 | 프레임 처리의 영향을 받으므로 정확한 실시간 마감 보장 아님 |
- Tick
- 알맞은 작업: 연속 이동·보간처럼 주기적으로 갱신할 계산주의할 경계: 활성화·실행 간격·Tick Group·선행 의존성 확인
- 이벤트
- 알맞은 작업: 입력·충돌·상태 변경에 대한 반응주의할 경계: 사건이 발생하고 연결된 대상이 있을 때 호출
- 타이머
- 알맞은 작업: 일정 간격의 갱신이나 지연 작업주의할 경계: 프레임 처리의 영향을 받으므로 정확한 실시간 마감 보장 아님
컴포넌트는 로직을 담는 단위입니다. 필요한 컴포넌트 안에서도 Tick·이벤트·타이머를 선택합니다. 이 비교는 엔진 전체의 고정된 프레임 실행 순서가 아닙니다.
주요 클래스 구조와 역할
언리얼 엔진의 게임 루프와 밀접하게 연관되어 게임의 전반적인 흐름을 제어하는 몇 가지 핵심 클래스들이 있습니다.
이 클래스들은 특정 역할을 전담하며, 서로 유기적으로 협력하여 하나의 게임을 완성합니다.
UGameInstance
- 역할:
UGameInstance는 게임 전체의 수명 주기 동안 유지되는 상위 레벨 객체입니다. 일반적인 실행 컨텍스트에서는 게임이 시작될 때 생성되어 게임이 종료될 때까지 유지됩니다. - 특징: 레벨이 변경되어도 파괴되지 않기 때문에, 같은 게임 인스턴스에서 레벨 전환을 넘어 유지할 데이터(예: 진행 상황, 설정, 세션 정보)를 관리할 수 있습니다. PIE 인스턴스마다 별도로 존재하며, 자동 네트워크 복제나 디스크 저장을 제공하는 것은 아닙니다.
- 활용: 게임 전반에 걸친 데이터 관리, 세션 관리, 로딩 화면 전환, 설정 저장/로드 등에 사용됩니다.
AGameModeBase / AGameMode
- 역할:
AGameModeBase(또는 더 구체적인 게임 유형에 맞는AGameMode)는 게임의 규칙과 흐름을 정의하는 클래스입니다. 예를 들어, 어떤 캐릭터 클래스를 사용할지, 플레이어의 스폰 지점은 어디인지, 점수 계산 방식은 무엇인지, 게임의 승리/패배 조건은 무엇인지 등을 설정합니다. - 특징:
AGameMode는 서버에서만 존재하며, 클라이언트에는 복제되지 않습니다. 이는 게임 규칙이 서버에서 단일하게 관리되어야 함을 의미합니다. 클라이언트에서는AGameStateBase(후술)가 게임 상태 정보를 공유받습니다. - 활용: 게임 모드 변경, 플레이어 접속/종료 처리, 게임 시작/종료 로직, 기본 Pawn/Controller/HUD 클래스 지정 등에 사용됩니다. 실제 UI 표시와 입력 처리는 보통 클라이언트의 PlayerController, HUD, Widget 쪽에서 담당합니다.
AGameStateBase / AGameState
- 역할:
AGameStateBase(또는AGameState)는 게임의 현재 상태 정보를 모든 클라이언트와 공유하는 역할을 합니다. 즉, 게임의 규칙(AGameMode)에 따라 변동되는 플레이어 점수, 남은 시간, 현재 라운드, 참여 플레이어 목록 등과 같은 정보를 담고, 이를 모든 플레이어에게 전달합니다. - 특징: GameState는 서버에서 클라이언트로 상태를 전달하는 복제 경로를 제공합니다. 직접 추가한 변수도 자동 전송되는 것은 아니므로 복제 선언·등록 등 필요한 설정을 해야 합니다.
- 활용: 점수판 업데이트, 라운드 정보 표시, 게임 진행 상태 표시 등에 사용됩니다.
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(사운드) 등이 있습니다. - 활용: 액터의 시각적 표현, 충돌 감지, 물리 시뮬레이션, 카메라, 사운드, 커스텀 로직 등 특정 모듈화된 기능을 구현할 때 사용됩니다.
데이터를 둘 위치
클래스 이름을 외우기보다 데이터가 어느 실행 환경에서 얼마나 오래 필요한지부터 묻습니다.
UNREAL C++ · CH2-4
데이터의 수명과 공유 범위로 위치 결정
레벨 간 유지, 서버 규칙, 공유 상태, 로컬 입력, 월드 객체의 데이터는 필요한 수명과 실행 환경이 다릅니다.
| 데이터 예 | 둘 위치 | 수명·공유 범위 |
|---|---|---|
| 레벨 간 선택값 | GameInstance | 같은 게임 인스턴스에서 유지; 복제·디스크 저장은 별도 |
| 승리 규칙 | GameMode | 서버 권한에서 판단; 원격 클라이언트에 인스턴스 복제 안 함 |
| 현재 라운드 | GameState | 클라이언트에 공유할 상태; 사용자 정의 필드는 복제 설정 필요 |
| 플레이어별 점수 | PlayerState | 해당 플레이어의 공유할 상태; Pawn 재생성과 분리 가능 |
| 로컬 입력·UI | PlayerController | 로컬 플레이어의 처리와 서버 측 제어를 구분 |
| 몸체·개별 기능 | Pawn·컴포넌트 | 스폰·파괴되는 월드 대상의 수명에 맞춰 관리 |
- 레벨 간 선택값
- 둘 위치: GameInstance수명·공유 범위: 같은 게임 인스턴스에서 유지; 복제·디스크 저장은 별도
- 승리 규칙
- 둘 위치: GameMode수명·공유 범위: 서버 권한에서 판단; 원격 클라이언트에 인스턴스 복제 안 함
- 현재 라운드
- 둘 위치: GameState수명·공유 범위: 클라이언트에 공유할 상태; 사용자 정의 필드는 복제 설정 필요
- 플레이어별 점수
- 둘 위치: PlayerState수명·공유 범위: 해당 플레이어의 공유할 상태; Pawn 재생성과 분리 가능
- 로컬 입력·UI
- 둘 위치: PlayerController수명·공유 범위: 로컬 플레이어의 처리와 서버 측 제어를 구분
- 몸체·개별 기능
- 둘 위치: Pawn·컴포넌트수명·공유 범위: 스폰·파괴되는 월드 대상의 수명에 맞춰 관리
데이터 배치의 예시입니다. 복제 조건과 레벨 이동·재접속 시 보존 정책은 게임이 명시해야 합니다. 오래 사는 객체의 참조도 월드 액터의 생존을 보장하지 않습니다.
월드의 액터 참조를 오래 유지되는 객체에 보관하더라도 액터의 생존이 연장되는 것은 아닙니다. 타이머와 델리게이트 등은 EndPlay를 비롯한 적절한 수명 경계에서 해제하고, 이후 사용할 참조의 유효성을 확인합니다.
표의 PlayerState는 플레이어별로 공유할 상태를 담는 범위입니다. 현재 몸체의 Pawn과 분리해 점수 등을 관리할 수 있지만, 재접속·레벨 이동 때 어떤 데이터를 보존할지와 사용자 정의 필드의 복제는 별도 정책입니다.
상태와 갱신 방법 판단하기
같은 게임 인스턴스에서 맵을 바꿔도 유지할 난이도 선택값, 서버가 결정하고 여러 클라이언트가 읽을 현재 라운드, 소지품 수가 바뀔 때만 다시 그리는 로컬 UI가 있습니다. 앞의 두 값은 각각 어느 범위에 둘까요? 마지막 UI 갱신은 매 프레임 Tick과 상태 변경 이벤트 중 무엇부터 검토할까요? GameInstance에 값을 두면 다른 클라이언트와 디스크에도 자동으로 저장되는지도 설명해 보세요.
수명·공유·갱신의 풀이
난이도 선택값은 같은 GameInstance의 맵 전환을 넘는 수명에, 공유할 현재 라운드는 GameState의 복제 경로에 둡니다. 직접 만든 라운드 필드는 복제 선언·등록 등 필요한 설정도 해야 합니다. 소지품 수가 바뀔 때만 갱신하면 상태 변경 이벤트부터 검토하고, 연속 보간이 추가될 때 필요한 주기 계산을 따로 둡니다. GameInstance는 자동 네트워크 복제나 디스크 저장을 제공하지 않으므로 그 두 경로는 별도로 구성합니다.
다음 장에서는 APawn, ACharacter, AController의 역할과 구현을 이어갑니다.