본 내용은 "언리얼 엔진으로 배우는 게임 디자인 패턴"을 간략하게 정리한 내용이다.

UE5 핵심 기술
UE5는 UE4에 비해 렌더링과 월드 표현 측면에서 많은 핵심 기술을 도입했다. 대표적으로 Nanite와 Lumen이 있다.
- Nanite는 언리얼 엔진의 가상화된 지오메트리 시스템이다. 매우 높은 디테일의 메시를 효율적으로 렌더링할 수 있도록 설계되어 있으며, 픽셀 단위에 가까운 디테일과 많은 수의 오브젝트를 처리하는 데 강점이 있다. 흔히 “폴리곤 수가 많은 모델을 부담 없이 쓸 수 있게 해주는 기술”로 이해하면 된다.
- Lumen은 UE5의 동적 글로벌 일루미네이션 및 반사 시스템이다. 장면 정보를 계산할 때 먼저 스크린 트레이스를 활용하고, 이후 더 신뢰도 높은 방식으로 소프트웨어 또는 하드웨어 레이 트레이싱을 사용한다. 또한 Surface Cache와 거리장 기반 데이터가 중요한 역할을 한다. 즉, “실시간으로 더 자연스러운 간접광과 반사 효과를 제공하는 시스템”이라고 정리하는 편이 정확하다.
게임 엔진 레이어 아키텍처
대부분의 게임 엔진은 여러 계층이 쌓인 구조로 이해할 수 있다.
각 레이어는 담당 역할이 다르며, 분리가 잘 되어 있을수록 유지보수와 확장이 쉬워진다.
예를 들어 새로운 그래픽 API나 플랫폼을 지원해야 한다면, 전체 시스템을 갈아엎기보다 플랫폼/렌더링에 가까운 레이어만 교체하거나 수정하는 식으로 대응할 수 있다.
엔진마다 구체적인 명칭과 구조는 다르지만, 보통 아래와 같이 정리할 수 있다.
| 툴 | 표준적인 작업의 속도를 높여주는 편집기 기능 전부. 일반적으로 엔진은 여기까지 갖춘 상태로 판매된다. |
| 게임플레이 | 제작하는 게임에 맞는 인터랙션 메카닉을 구현하기 위해 만들어진 모든 커스템 시스템 |
| 기능 | 입력 캡처나 물리 처리 등과 같은 모든 자동 내부 시스템이 처리되는 곳 |
| 리소스 | 메모리 관리와 에셋 스트리밍이 처리되는 곳 |
| 플랫폼 | 그래픽 파이프라인을 정의하는 것이 기본이며 엔진이 지원하는 경우 플랫폼 네이티브 통합을 구축한다. |
| 기초 | 핵심 종속 라이브러리 전부. 예컨대 에디터용 UI 프레임워크와 수학 라이브러리 등 |
퍼지 레이어
언리얼이 레이어에 명시적인 레이블을 붙이지는 않지만, C++과 블루프린트(BP)가 게임플레이를 처리하는 방식에서 실제 레이어가 작동함을 확인할 수 있다.
기본적으로 C++에서 생성한 함수는 블루프린트에서 액세스할 수 있지만, 그 역은 성립하지 않는다. 이는 동작에 순서가 있어 신호가 한 방향으로 전달됨을 보여준다. 게임플레이 레이어 간의 이런 내부 분리를 '퍼지 레이어(Fuzzy Layer)'라고 한다.
퍼지 레이어 설계가 다소 불편할 수는 있지만, 결과적으로 프로그래머와 디자이너 간의 마찰을 줄여준다. 언리얼은 이런 구상을 돕기 위해 신호 전달 방식을 정의하는 매크로 지정자를 제공한다.
언리얼에서는 일반적으로 다음과 같은 흐름이 많이 사용된다.
- C++: 시스템의 뼈대, 성능이 중요한 로직, 재사용 가능한 기반 클래스
- Blueprint: 빠른 프로토타이핑, 반복 조정, 디자이너 친화적 작업
공식 문서도 C++ 클래스 위에 Blueprint를 확장하는 방식을 권장한다. 즉, 프로그래머가 기반 클래스를 만들고, 디자이너가 Blueprint에서 이를 조립·조정하는 구조다.
다만 여기서 중요한 점은, 관계가 단순히 “C++ → Blueprint 단방향”만은 아니라는 것이다.
- UPROPERTY(...)를 사용하면 C++ 멤버 변수를 Blueprint와 에디터에 노출할 수 있다.
- UFUNCTION(...)를 사용하면 C++ 함수를 Blueprint에서 호출할 수 있다. 예: BlueprintCallable
- 반대로 BlueprintImplementableEvent, BlueprintNativeEvent를 사용하면 C++ 쪽에서 Blueprint 구현을 확장 포인트로 호출할 수도 있다.
언리얼은 C++를 기반으로 Blueprint를 확장하는 구조가 기본이지만, 적절한 지정자를 사용하면 C++와 Blueprint 사이에 명확한 인터페이스를 만들 수 있다.
프로퍼티 지정자와 함수 지정자
1. 프로퍼티 지정자
프로퍼티 지정자는 UPROPERTY(...)에 붙는 옵션으로,
해당 변수가 에디터에서 보이는지, 수정 가능한지, Blueprint에서 읽기/쓰기 가능한지 등을 정의한다.
예를 들면 다음과 같다.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Stat")
float MoveSpeed = 600.f;
이 경우 MoveSpeed는:
- 에디터 어디서든 수정 가능하고
- Blueprint에서 읽고 쓸 수 있으며
- Stat 카테고리로 정리된다
대표적으로 많이 쓰는 지정자는 다음과 같다.
- EditAnywhere
- EditDefaultsOnly
- VisibleAnywhere
- BlueprintReadOnly
- BlueprintReadWrite
공식 문서는 UPROPERTY가 리플렉션 시스템에 인식되는 변수 선언 방식이라고 설명한다.
언리얼 엔진 UProperty | 언리얼 엔진 5.4 문서 | Epic Developer Community
게임플레이 클래스에 대한 프로퍼티 생성 및 구현 관련 레퍼런스입니다.
dev.epicgames.com
2. 함수 지정자
함수 지정자는 UFUNCTION(...)에 붙는 옵션으로,
함수가 Blueprint에서 호출 가능한지, Blueprint가 구현을 덮어쓸 수 있는지, 네트워크 RPC인지 등을 정의한다.
예를 들면 다음과 같다.
UFUNCTION(BlueprintCallable, Category="Action")
void Attack();
이 함수는 Blueprint에서 호출할 수 있다.
또는
UFUNCTION(BlueprintImplementableEvent, Category="Action")
void OnAttackStarted();
이 경우 함수 본체는 C++에 작성하지 않고, Blueprint에서 구현한다.
즉, C++가 Blueprint를 호출하는 확장 지점이 된다.
실무에서 자주 보게 되는 지정자는 다음과 같다.
- BlueprintCallable
- BlueprintPure
- BlueprintImplementableEvent
- BlueprintNativeEvent
언리얼 엔진의 UFunctions | 언리얼 엔진 5.4 문서 | Epic Developer Community
게임플레이 클래스용 함수 생성 및 구현에 대한 개요입니다.
dev.epicgames.com
블루프린트를 C++로 변환하기
왜 Blueprint로 먼저 만들고, 나중에 C++로 옮길까?
대형 팀에서는 기능 하나를 다음 순서로 개발하는 경우가 많다.
- 디자이너나 기획자가 Blueprint로 빠르게 프로토타입을 만든다.
- 플레이 테스트와 반복 조정을 통해 재미와 요구사항을 검증한다.
- 방향이 확정되면 프로그래머가 이를 C++ 기반 구조로 재작성하거나 최적화한다.
이 방식의 장점은 명확하다.
- 초기에 빠르게 검증할 수 있다
- 비개발 직군도 실험에 참여할 수 있다
- 확정된 뒤에는 C++로 구조화해 성능과 유지보수성을 확보할 수 있다
효율적인 변환 순서
남이 만든 Blueprint를 C++로 변환해야 할 때, 가장 현실적인 방법은 노드 단위로 대응되는 C++ API를 찾는 것이다.
1. 노드의 Target 클래스를 확인한다
Blueprint 노드 위에 마우스를 올리면, 해당 노드가 어떤 클래스의 함수인지 확인할 수 있다.
많은 노드는 특정 클래스의 멤버 함수이거나, Blueprint Function Library의 정적 함수다.
즉, 먼저 확인해야 할 것은 다음이다.
- 이 노드가 어느 클래스에 속한 함수인지
- 혹은 어느 Function Library에 속한 정적 함수인지

2. 공식 문서에서 클래스/API를 찾는다
언리얼 엔진 문서 검색에서 다음 방식으로 찾으면 된다.
- 검색 대상: Unreal Engine / Documentation / API
- 언어: English
- 검색어: U클래스명 또는 함수명
예를 들어 Target 클래스가 GameplayStatics 계열이라면
문서에서는 보통 UGameplayStatics처럼 U 접두사가 붙은 C++ 클래스로 찾게 된다.

3. API 문서에서 함수와 헤더 경로를 확인한다
해당 클래스 문서에 들어가면 함수 목록과 함께 다음 정보를 확인할 수 있다.
- 함수 시그니처
- 소속 클래스
- 헤더 파일 경로 (#include)
- 모듈 정보
이 정보를 보면 Blueprint 노드를 C++ 코드로 옮길 때 필요한 기반이 거의 나온다.
예를 들어 실제 작업에서는 다음 순서로 이어진다.
- Blueprint 노드 기능 확인
- Target 클래스 확인
- API 문서에서 대응 함수 검색
- 헤더 include
- 호출 방식과 인자 구조를 C++로 재작성

이 과정을 반복하면 어느 시점부터는
“이 노드는 아마 UKismetSystemLibrary 쪽이겠구나”,
“이건 UGameplayStatics에 있겠구나” 같은 감이 생긴다.
1. 모든 Blueprint 노드가 1:1로 단순 대응되지는 않는다
대부분의 노드는 C++ 함수와 대응되지만, 일부는 다음과 같은 형태일 수 있다.
- 매크로 형태
- 컴파일러가 특별 처리하는 노드
- K2 계열 확장 노드
- 여러 내부 호출을 묶어 보여주는 편의 노드
그래서 “노드 하나 = 함수 하나”가 항상 성립하는 것은 아니다.
하지만 대부분의 실무용 노드들은 C++ API 문서 추적으로 접근 가능하다.
2. Blueprint Function Library도 자주 확인해야 한다
특정 객체에 속하지 않는 유틸리티성 노드는 UBlueprintFunctionLibrary 기반 클래스에 들어있는 경우가 많다.
대표적으로 여러 Kismet 계열 함수들이 이런 구조다.
3. 완전한 “변환”보다 “재설계”가 더 중요할 때도 많다
프로토타입 Blueprint는 검증을 목표로 작성되기 때문에:
- 중복 로직이 많고
- 책임 분리가 약하고
- 데이터 흐름이 즉흥적일 수 있다
따라서 C++로 옮길 때는 단순 복붙보다 아래를 먼저 보는 편이 좋다.
- 어떤 데이터가 상태인가
- 어떤 로직이 반복되는가
- 어떤 부분을 컴포넌트/서브시스템/베이스 클래스로 분리할 수 있는가
- 디자이너가 계속 조절해야 하는 값은 무엇인가
즉, Blueprint를 C++로 “번역”하는 것보다, 검증된 의도를 C++ 구조로 “재구성”하는 것이 더 중요하다.