본문 바로가기

Unreal Engine의 특징과 구조

언리얼 엔진의 리플렉션·컴포넌트·게임 프레임워크 구조를 연결해 에디터와 런타임의 역할을 이해합니다.

언리얼 C++를 처음 시작하면 C++ 문법보다 엔진 구조를 어디서부터 이해해야 할지가 먼저 막힙니다. 일반 C++ 프로젝트는 main 함수에서 시작해 내가 만든 객체만 다루지만, 언리얼에서는 리플렉션, 컴포넌트, 에디터 연동, 런타임 규칙이 함께 움직입니다. 그래서 코드를 많이 쓰기 전에 엔진이 어떤 단위로 동작하고 객체가 어떤 생명 주기를 갖는지를 먼저 잡아 두는 편이 이후의 학습을 훨씬 덜 헤매게 합니다.

이 글에서는 언리얼 엔진이 C++ 개발에서 어떤 점이 다른지, 그리고 이후 장에서 계속 만나게 될 액터, 컴포넌트, 월드, 게임 모드, 컨트롤러, 폰이 서로 어떤 관계인지를 정리합니다. 이 관계를 한 그림으로 이해해 두면, 나중에 클래스 설계나 네트워크 로직을 배울 때도 개념이 흩어지지 않고 이어집니다.


언리얼 엔진이 제공하는 것

언리얼 엔진(Unreal Engine)은 Epic Games가 개발한 실시간 3D 콘텐츠 제작 도구이자 게임 엔진입니다. 게임뿐 아니라 건축 시각화, 영화와 방송 제작 같은 분야에서도 쓰이는데, 이렇게 활용 범위가 넓은 것은 엔진이 기능을 확장하기 쉽고 여러 제작 도구와 파이프라인으로 연결되기 때문입니다.

먼저 꼽을 특징은 실시간 렌더링의 품질입니다. 루멘(Lumen)은 동적 전역 조명과 반사를, 나나이트(Nanite)는 가상화된 지오메트리 처리를 제공해 많은 폴리곤을 가진 장면도 실시간으로 그릴 수 있게 합니다. 다만 두 기능 모두 지원하는 플랫폼과 렌더링 경로가 정해져 있고 성능 비용도 있으므로, 대상 기기와 성능 예산을 확인한 뒤 쓸 기능을 정합니다.

두 번째 특징은 블루프린트(Blueprint) 기반 비주얼 스크립팅입니다. 노드를 연결해 로직을 만들 수 있으므로 C++을 직접 작성하지 않는 아티스트나 기획자도 개발 과정에 빠르게 참여할 수 있습니다. 이 교재는 C++을 중심으로 하지만, 실무에서는 C++로 공통 구조를 만들고 블루프린트로 값과 연출을 조정하는 식으로 둘을 함께 쓸 때 작업 속도가 가장 빠릅니다.

세 번째는 여러 플랫폼 지원입니다. 언리얼 엔진은 PC, 콘솔, 모바일, VR/AR 같은 주요 플랫폼을 지원하므로, 한 프로젝트를 여러 플랫폼에 맞춰 배포하면 개발 시간과 비용을 줄일 수 있습니다. 하지만 이것이 하나의 구성을 그대로 모든 기기에 배포할 수 있다는 뜻은 아닙니다. 대상 플랫폼마다 SDK와 개발 권한, 지원하는 엔진 버전, 쓸 수 있는 렌더링 기능이 다르기 때문입니다.

마지막으로 에디터와 제작 도구가 엔진 안에 함께 들어 있습니다. 레벨 에디터를 중심으로 머티리얼 에디터, 애니메이션 도구, 나이아가라(Niagara) 이펙트 에디터처럼 콘텐츠 제작에 필요한 도구를 언리얼 에디터 안에서 바로 쓸 수 있습니다. 도구가 한곳에 모여 있으므로 코드를 고치고, 에디터에서 값을 바꾸고, 바로 플레이해 보는 반복이 짧아집니다.


C++ 개발에서 먼저 알아야 할 핵심 구조

언리얼 엔진은 규모가 큰 만큼 구조도 체계적입니다. 그중 C++ 개발을 시작하면서 가장 먼저 이해해야 할 개념은 액터, 컴포넌트, 월드, 게임 모드, 플레이어 컨트롤러, 폰입니다.

액터(Actor)는 월드에 배치하거나 스폰하는 객체의 기본 타입입니다. 플레이어 캐릭터, 적, 나무, 건물, 광원 같은 것들을 액터로 만듭니다. 하지만 액터 안에 들어 있는 컴포넌트나 위치가 없는 데이터까지 모두 별도의 액터인 것은 아닙니다. 또 액터의 위치, 회전, 스케일은 액터가 직접 저장하는 값이 아니라, 루트 SceneComponent가 있으면 그 컴포넌트의 트랜스폼(Transform)을 쓰는 것입니다.

컴포넌트(Component)는 액터에 기능을 더하는 부품입니다. 액터는 여러 컴포넌트를 조합해 복합적인 동작을 만듭니다. 예를 들어 StaticMeshComponent는 3D 모델을 화면에 표시하고, CameraComponent는 카메라 시점을 맡습니다. C++에서는 이렇게 컴포넌트를 조합하는 것이 기능을 확장하는 기본 방법입니다.

월드(World)는 액터들이 배치되고 상호작용하는 공간입니다. 월드는 하나의 영속 레벨(persistent level)과 추가로 불러오는 레벨들을 포함할 수 있고, 각 월드는 자신의 물리 시뮬레이션, 렌더링, 게임플레이 규칙을 관리합니다. 에디터에서 편집하는 월드와 에디터에서 플레이(Play In Editor, PIE)를 눌렀을 때 실행되는 월드도 서로 다른 월드입니다.

게임 모드(GameMode)는 게임 전체의 규칙을 정하는 클래스입니다. 어떤 캐릭터를 스폰할지, 점수를 어떻게 관리할지, 승리와 패배 조건이 무엇인지를 게임 모드에서 정합니다. 게임 모드는 서버에만 존재하며 클라이언트에는 복제되지 않습니다. 그래서 클라이언트가 알아야 하는 진행 상태는 2장과 8장에서 다룰 GameState 같은 다른 클래스로 전달합니다.

플레이어 컨트롤러(PlayerController)는 플레이어의 입력을 받아 게임과 상호작용하게 하는 클래스입니다. 키보드, 마우스, 게임패드의 입력을 처리하고, 그 결과를 조종하는 대상에게 전달해 움직이거나 행동하게 합니다.

폰(Pawn)은 플레이어나 AI가 조종할 수 있는 액터 타입입니다. PlayerController나 AI 컨트롤러가 폰에 빙의(Possess)해 조종합니다. 폰 가운데 캐릭터(Character)는 CharacterMovementComponent를 기본으로 가지고 있어, 걷기와 점프 같은 사람형 이동을 구현할 때 알맞습니다.

UNREAL C++ · CH1-1

포함·제어·상속은 서로 다른 관계입니다

월드에는 액터 인스턴스가 있고, 액터는 컴포넌트로 기능을 구성합니다. Controller의 Possess는 Pawn을 제어하는 연결이며 클래스 상속과 다릅니다.

서버 World의 인스턴스 포함과 제어 관계예시서버World영역에GameMode와PlayerController및CharacterPawn이있다. Character영역안루트SceneComponent와탄약ActorComponent가소속된다. PC의Possess는제어,GameMode점선은스폰클래스규칙이며컴포넌트포함·클래스상속·생성순서와다른관계다.서버 World · 인스턴스의 예PlayerController현재 Pawn 제어GameMode서버의 스폰 규칙Character 인스턴스 · Pawn루트 SceneComponentTransform탄약 기능ActorComponentTransform 불필요Possess스폰 규칙영역은 포함 · 실선은 제어 · 점선은 스폰 규칙
서버 World의 인스턴스 포함과 제어 관계예시서버World영역에GameMode와PlayerController및CharacterPawn이있다. Character영역안루트SceneComponent와탄약ActorComponent가소속된다. PC의Possess는제어,GameMode점선은스폰클래스규칙이며컴포넌트포함·클래스상속·생성순서와다른관계다.서버 World · 인스턴스 예GameMode서버의 스폰 규칙PlayerController현재 Pawn 제어Character · Pawn루트SceneComponentTransform탄약 기능ActorComponentTransform 불필요Possess영역: 포함 · 실선: 제어점선: 서버의 스폰 규칙
Character에서 Actor까지의 부모 타입Character는Pawn을상속하고Pawn은Actor를상속한다. 화살표는자식타입에서부모타입을향하며World인스턴스포함이나ControllerPossess가아니다.Character자식 타입PawnCharacter의 부모ActorPawn의 부모상속상속화살표: 자식 타입 → 부모 타입
Character에서 Actor까지의 부모 타입Character는Pawn을상속하고Pawn은Actor를상속한다. 화살표는자식타입에서부모타입을향하며World인스턴스포함이나ControllerPossess가아니다.
Character자식 타입
상속상속Character · 자식 타입 → Pawn · Character의 부모
PawnCharacter의 부모
상속상속Pawn · Character의 부모 → Actor · Pawn의 부모
ActorPawn의 부모

화살표: 자식 → 부모 타입Possess·부착 관계와 다름

포함 관계 · 실행 중인 월드

World → 레벨에 존재하는 Actor 인스턴스 → 액터가 소유한 Component

SceneComponent

Transform과 부착 계층을 가집니다. Root SceneComponent가 있으면 Actor의 위치·회전·스케일은 이 루트에서 옵니다.

ActorComponent

액터에 재사용할 기능을 조립합니다. 모든 ActorComponent가 공간 Transform을 가지는 것은 아닙니다.

제어 관계 · Controller → Pawn

PlayerController는 플레이어의 입력·판단을 Pawn의 동작으로 연결합니다. AIController도 Pawn을 제어할 수 있습니다.

Controller가 Pawn의 부모 클래스나 SceneComponent가 되는 것은 아닙니다.

상속 관계 · Actor → Pawn → Character

Pawn은 제어할 수 있는 Actor 타입입니다. Character는 Pawn을 상속하고 CharacterMovementComponent를 포함합니다.

클래스의 종류 관계이며 월드 아웃라이너의 부착 트리와 다릅니다.

서버 규칙 · GameMode

스폰할 클래스·승리 조건 같은 게임 규칙을 맡습니다. 원격 클라이언트에 GameMode 인스턴스를 자동 복제하는 관계가 아닙니다.

편집 월드와 PIE 실행 월드는 구분합니다. 이 그림은 역할 관계를 보여주며 객체 생성 순서나 전체 엔진 계층을 나타내지 않습니다.

위 그림은 이 개념들 사이의 세 가지 관계를 구분해 보여 줍니다. 첫째는 포함 관계입니다. 서버 월드 영역 안에 GameMode, PlayerController, Character 인스턴스가 있고, Character 영역 안에는 트랜스폼을 가진 루트 SceneComponent와 트랜스폼이 필요 없는 탄약 기능 ActorComponent가 들어 있습니다. 그림 아래의 설명처럼 루트 SceneComponent가 있으면 액터의 위치, 회전, 스케일은 이 루트에서 오고, 모든 ActorComponent가 공간 트랜스폼을 가지는 것은 아닙니다.

둘째는 제어 관계로, 그림의 실선 화살표인 Possess입니다. PlayerController는 플레이어의 입력과 판단을 현재 조종하는 Pawn의 동작으로 연결하며, AIController도 같은 방식으로 Pawn을 조종할 수 있습니다. 이 연결은 실행 중에 맺는 관계이므로, Controller가 Pawn의 부모 클래스가 되거나 Pawn에 붙은 SceneComponent가 되는 것은 아닙니다. GameMode에서 나오는 점선은 어떤 클래스를 스폰할지 정하는 서버의 규칙을 뜻합니다.

셋째는 상속 관계입니다. 그림의 다른 부분은 Character가 Pawn을 상속하고 Pawn이 Actor를 상속한다는 것을 자식 타입에서 부모 타입으로 향하는 화살표로 보여 줍니다. 이것은 클래스의 종류를 나타내는 관계이므로, 아웃라이너(그림의 월드 아웃라이너)에 보이는 부착 트리나 Controller의 Possess와는 다릅니다. 그림은 역할 사이의 관계만 보여 주며, 객체가 생성되는 순서나 엔진의 전체 클래스 계층을 나타내지는 않습니다.


탄약을 가진 캐릭터로 나눠 보기

플레이어 캐릭터가 이동하고 탄약을 가진다고 해 봅시다. 현재 월드에 있는 Character 인스턴스는 위치와 회전을 가진 대상이고, 이동은 CharacterMovementComponent가 맡습니다. 탄약 개수와 습득 규칙은 별도의 기능 컴포넌트로 나눌 수 있는데, 탄약을 관리하는 일에는 공간 트랜스폼이 필요 없으므로 ActorComponent로 만듭니다. 메시나 카메라처럼 위치가 있어야 하는 기능은 SceneComponent를 쓰므로, 둘을 구별해 두면 컴포넌트의 종류를 고르기 쉬워집니다.

플레이어가 실행 중에 다른 Pawn을 조종하게 되는 상황도 생각해 봅시다. 이때 일어나는 Possess는 현재 Controller가 조종하는 대상의 참조를 바꾸는 일이며, 클래스 상속과는 관계가 없습니다. 기존 Character 클래스의 부모 클래스가 바뀌는 것도 아닙니다. 지금은 생성 코드를 쓰기보다, 어느 인스턴스가 어느 기능을 가지고 있고 누가 그것을 조종하는지를 말로 설명할 수 있으면 충분합니다.

새 기능을 만들 때마다 다음 질문으로 나눠 보면 이후 장의 설계가 쉬워집니다. 월드에 배치할 액터인지, 액터에 붙일 컴포넌트인지, 위치가 없는 데이터인지를 먼저 구분합니다. 그다음 Controller가 맡을 입력과 판단, Pawn이 맡을 동작, GameMode가 맡을 서버 규칙을 나눕니다. 엔진 객체와 일반 C++ 객체는 클래스 이름의 접두사와 생성, 수명을 관리하는 방식이 다르다는 점도 2장에서 확인합니다. 조정할 값과 공개할 함수를 정할 때는 에디터의 기본값, 배치한 인스턴스의 값, 실행 중에 바뀌는 값을 구분하고, C++에 둘 공통 구조와 블루프린트에서 조정할 부분을 나눠 둡니다.


정리

  • 언리얼 엔진은 루멘과 나나이트 같은 실시간 렌더링 기능, 블루프린트 비주얼 스크립팅, 여러 플랫폼 지원, 에디터에 통합된 제작 도구를 제공합니다. 렌더링 기능과 플랫폼 배포는 대상 기기의 조건과 성능 예산에 맞춰 고릅니다.
  • 액터는 월드에 배치하거나 스폰하는 객체이고, 위치와 회전은 루트 SceneComponent의 트랜스폼에서 옵니다. 컴포넌트는 액터에 기능을 더하는 부품이며, 모든 컴포넌트가 트랜스폼을 가지는 것은 아닙니다.
  • 월드는 영속 레벨과 추가 레벨을 포함하는 공간이고, 편집 월드와 PIE 실행 월드는 다릅니다.
  • 게임 모드는 서버에만 있는 규칙 클래스이고, 플레이어 컨트롤러는 입력을 받아 폰을 조종합니다. 캐릭터는 이동 컴포넌트를 갖춘 폰입니다.
  • 포함, 제어(Possess), 상속은 서로 다른 관계입니다. Possess는 실행 중의 제어 연결이며 클래스 상속이나 컴포넌트 포함을 바꾸지 않습니다.

연습 문제

1. 세 가지 기능의 타입

월드에 배치할 탄약 Pickup, 현재 Character에 붙일 탄약 관리 기능, 위치가 없는 저장 데이터가 있습니다. 세 가지를 모두 Actor로 만들어야 할까요? 각각 어떤 타입으로 시작하면 좋을지 이유와 함께 설명해 보세요.

해설 보기

모두 Actor일 필요는 없습니다. Pickup은 월드에 놓이고 플레이어와 겹치는지 판단해야 하므로 월드에 배치할 Actor로 시작할 수 있습니다.

탄약 관리 기능은 위치가 필요 없고 Character에 붙어서 동작하므로 ActorComponent로 분리합니다. 위치가 없는 저장 데이터는 월드에 놓일 이유가 없으므로 Actor가 필요하지 않으며, 7장에서 다룰 SaveGame처럼 목적에 맞는 타입을 고릅니다.

2. 새 Pawn에 빙의하면

Pawn A를 조종하던 Controller가 Pawn B에 새로 빙의했습니다. 이때 Pawn A가 Controller의 자식 클래스로 바뀌거나, Pawn B가 Controller의 컴포넌트가 될까요?

해설 보기

둘 다 아닙니다. Possess는 실행 중에 Controller가 조종하는 대상을 바꾸는 제어 연결입니다. Controller가 가리키는 Pawn의 참조가 A에서 B로 바뀔 뿐, 클래스 상속이나 컴포넌트 포함 관계는 그대로입니다.

그림에서 보았듯이 상속은 클래스의 종류 관계, 포함은 액터가 컴포넌트를 소유하는 관계, Possess는 실선으로 그린 제어 관계이므로 세 가지를 따로 판단해야 합니다.

언리얼 엔진의 게임플레이 클래스가 맡는 역할은 Epic Games의 Gameplay Framework 문서에서 더 자세히 확인할 수 있습니다.

다음 글 개발 환경 설정에서는 언리얼 엔진과 Visual Studio를 설치하고, C++ 프로젝트를 빌드할 수 있는 환경을 갖춥니다.