액터 블루프린트의 기본 개념
월드에 배치되는 액터 블루프린트의 구조와 역할을 이해하고 컴포넌트·변수·이벤트를 함께 구성합니다.
2장에서 변수와 함수, 이벤트, 흐름 제어를 익혔다면 이제 이것들을 어디에 담을지 정할 차례입니다. 문이 열리는 로직은 문 안에, 체력을 깎는 로직은 캐릭터 안에 있어야 나중에 찾기 쉽고, 같은 문을 레벨 여러 곳에 놓을 때도 로직을 다시 만들 필요가 없습니다. 언리얼 엔진에서 게임 월드를 이루는 이 "담는 단위"가 액터(Actor)이고, 액터가 무엇을 가지고 어떻게 행동할지 정의한 것이 액터 블루프린트(Actor Blueprint)입니다.
이 글에서는 액터가 무엇인지, 액터 블루프린트가 클래스와 인스턴스라는 관계로 어떻게 재사용되는지, 그리고 컴포넌트와 에디터 패널이 액터를 구성하는 데 어떤 역할을 하는지 살펴봅니다.
월드에 놓이는 단위, 액터
액터는 레벨에 배치하거나 플레이 중에 생성(spawn)할 수 있는 오브젝트의 기본 클래스입니다. 플레이어 캐릭터, 적 몬스터, 문, 램프, 바닥처럼 월드에 있는 대부분의 게임 오브젝트를 액터로 구성합니다. 다만 액터가 꼭 화면에 보여야 하는 것은 아닙니다. 소리가 나는 위치나 이벤트를 감지하는 투명한 영역처럼 보이지 않는 액터도 있습니다.
반대로 언리얼 엔진의 모든 오브젝트가 액터인 것도 아닙니다. 엔진의 객체는 UObject에서 출발하며, AActor와 UObject의 차이점에서 다루듯 그중 월드에 놓이는 것만 액터입니다. 화면의 UI 위젯은 액터가 아니고, 월드 자체를 나타내는 UWorld도 액터가 아니라 액터들을 담는 쪽입니다.
액터는 3차원 공간의 위치(Location), 회전(Rotation), 크기(Scale)를 가지는데, 이 값은 액터 자체가 아니라 루트 씬 컴포넌트(root scene component)의 변환(transform)으로 표현됩니다. 그래서 액터는 기능을 직접 다 가지기보다 여러 컴포넌트를 담는 그릇에 가깝습니다. 액터는 게임 플레이 중에 생성될 수도 있고 파괴될 수도 있으며, 그 시점에 실행되는 이벤트는 액터 초기화와 종료 이벤트에서 다룹니다.
액터 블루프린트는 액터의 설계도
액터 블루프린트는 월드에 배치할 액터의 설계도입니다. 공식 문서의 표현대로 블루프린트 클래스는 새로운 액터의 종류를 정의하며, 그 액터가 어떤 시각 요소(3D 모델, 파티클 등)와 기능(움직임, 상호작용 등), 로직(AI, 이벤트 반응 등)을 가질지 정합니다. 블루프린트를 만들 때 고르는 부모 클래스가 무엇을 물려받을지를 정하는데, Actor를 고르면 월드에 놓이는 기본 액터가 되고, Pawn이나 Character를 고르면 조종하거나 걸어 다닐 수 있는 액터가 됩니다.
첫 번째 블루프린트 생성 및 실행에서 만든 BP_WelcomeMessage도 Actor를 부모로 한 액터 블루프린트였습니다. 이 블루프린트에는 BeginPlay에서 Print String을 실행하는 로직이 들어 있었고, 레벨에 배치한 뒤 플레이하자 게임이 시작될 때 메시지가 출력되었습니다. 이때 콘텐츠 브라우저에 있던 것은 설계도인 클래스이고, 레벨에 놓아 실제로 메시지를 출력한 것은 그 클래스로 만든 액터 하나였습니다.
한 클래스, 여러 인스턴스
액터 블루프린트는 클래스를 정의하고, 월드에 배치된 액터는 그 클래스의 인스턴스(instance)가 됩니다. 이 관계는 객체 지향 프로그래밍(Object-Oriented Programming, OOP)의 클래스와 객체 관계를 시각적으로 옮겨 놓은 것입니다. 같은 클래스로 만든 인스턴스는 구성과 로직을 공유하지만, 위치나 현재 상태 같은 값은 각자 따로 가집니다.
ONE CLASS · TWO WORLD INSTANCES
같은 문 클래스의 두 인스턴스는 각자 상태를 가집니다
BP_Door로 문 두 개를 배치하고, 플레이 중 문 B만 열었다고 가정합니다. 공유하는 설계와 각 인스턴스의 현재 값을 구분합니다.
클래스의 공통 구성과 배치된 인스턴스의 편집 대상을 구분합니다. 변수의 인스턴스 편집 허용 여부를 확인하며, B를 열었다는 사실만으로 A도 열리지는 않습니다.
위 그림은 BP_Door 클래스 하나로 문 두 개를 배치하고, 플레이 중에 문 B만 열었다고 가정한 상황입니다. 클래스 쪽에는 컴포넌트 구성과 OpenDoor 로직, 변수의 기본값이 있고, 클래스에서 문 A와 문 B로 향하는 화살표는 인스턴스를 만드는 생성 관계를 뜻합니다. 두 문은 각자 자신의 위치를 가지며, 현재 상태도 A는 닫힘, B는 열림으로 서로 다릅니다. 두 인스턴스 사이에는 서로의 상태를 바꾸는 실행 선이 없으므로, B를 열었다는 사실만으로 A가 함께 열리지는 않습니다.
이 구조 덕분에 재사용이 쉬워집니다. 문 블루프린트를 하나 잘 만들어 두면 게임의 모든 문에 같은 열고 닫는 기능을 쓸 수 있고, 클래스에서 로직을 고치면 그 클래스로 배치한 모든 문에 수정이 반영됩니다. 그림 아래의 설명처럼, 클래스가 정한 공통 구성과 배치된 인스턴스에서 따로 고칠 수 있는 값은 구분해야 합니다. 인스턴스마다 다르게 두고 싶은 변수는 디테일 패널에서 인스턴스 편집 가능(Instance Editable)을 켜 두어야 레벨에 놓인 문을 선택했을 때 그 값을 바꿀 수 있습니다.
컴포넌트로 기능을 조립하기
액터는 여러 컴포넌트(component)로 구성됩니다. 컴포넌트는 액터에 특정한 기능이나 속성을 더하는 독립된 부품입니다. 예를 들어 Static Mesh 컴포넌트는 3D 모델을 보여 주고, Rotating Movement 같은 이동 컴포넌트는 액터를 움직이며, Box Collision 컴포넌트는 다른 액터가 들어왔는지 감지합니다. 필요한 기능만 골라 붙이므로, 모든 기능을 가진 거대한 액터 하나를 만드는 것보다 구조가 단순해집니다.
블루프린트의 그래프는 이렇게 블루프린트에 노출된 기능을 노드로 조합합니다. 프로그래밍 경험이 없는 디자이너도 컴포넌트를 붙이고 노드를 이어 액터를 만들 수 있다는 것이 블루프린트의 장점입니다. 다만 필요한 기능이 블루프린트에 노출되어 있지 않다면 C++로 확장해야 할 수 있습니다. 컴포넌트를 추가하고 다루는 방법은 다음 글에서 자세히 봅니다.
에디터 패널을 액터의 관점에서 다시 보기
블루프린트 에디터 인터페이스에서 본 패널들을 이번에는 액터를 구성한다는 관점에서 다시 보겠습니다. 각 패널은 액터의 서로 다른 면을 다룹니다.
- 컴포넌트(Components) 패널: 이 액터가 가진 컴포넌트를 계층 구조로 보여 줍니다. 여기서 컴포넌트를 추가하고, 지우고, 부모와 자식 관계를 바꿔 액터의 기능을 구성합니다.
- 뷰포트(Viewport): 액터가 월드에서 어떤 모습일지 보여 줍니다. 컴포넌트의 위치, 회전, 크기를 직접 조절하며 배치할 수 있습니다.
- 이벤트 그래프(Event Graph): 이 액터가 어떤 이벤트에 반응해 어떤 로직을 실행할지 정합니다. 플레이어와의 상호작용, 다른 액터와의 충돌, 시간 경과 같은 이벤트에 대한 반응을 노드로 만듭니다.
- 디테일(Details) 패널: 선택한 컴포넌트나 변수, 노드의 속성을 보여 주고 편집합니다. 액터의 기본 속성이나 컴포넌트의 세부 값을 여기서 조정합니다.
- 마이 블루프린트(My Blueprint) 패널: 이 블루프린트에서 쓸 변수, 함수, 매크로, 이벤트 디스패처를 만들고 관리합니다.
첫 액터 블루프린트를 점검하는 기준
첫 액터 블루프린트를 만들 때는 기능을 늘리기 전에 구조부터 점검해 두어야 나중에 고치는 비용이 줄어듭니다. 먼저 컴포넌트를 보이는 것을 맡는 컴포넌트와 로직을 시작하게 하는 컴포넌트로 나누어 배치합니다. 문이라면 문짝을 보여 주는 메시와 플레이어가 다가왔는지 감지하는 충돌 영역을 따로 두는 식입니다. 그래야 문짝 모델을 바꾸어도 감지 범위가 함께 바뀌지 않습니다.
이벤트 그래프는 입력, 상태 변경, 출력의 순서로 구역을 나누고 주석(Comment)으로 묶습니다. 그리고 레벨 디자이너가 조정할 만한 값, 예를 들어 문이 열리는 각도나 속도는 변수로 만들어 디테일 패널에 노출합니다. 이렇게 하면 같은 클래스를 여러 상황에 재사용할 수 있습니다.
노드가 많아지면 디버깅하는 순서를 정해 두는 것도 도움이 됩니다. 이벤트 시작 노드에서부터 실행 경로를 먼저 따라가고, 분기 노드의 앞뒤에서 상태 값을 출력해 원인이 있는 범위를 좁힙니다. 기능을 고칠 때는 노드를 더 붙이기보다 기존 노드를 함수나 매크로로 정리하는 일을 먼저 합니다.
정리
- 액터는 레벨에 배치하거나 플레이 중에 생성할 수 있는 오브젝트의 기본 클래스이며, 모든
UObject가 액터인 것은 아닙니다. - 액터의 위치, 회전, 크기는 루트 씬 컴포넌트의 변환으로 표현되고, 액터는 여러 컴포넌트를 담는 그릇 역할을 합니다.
- 액터 블루프린트는 클래스이고, 레벨에 배치된 액터는 그 인스턴스입니다. 인스턴스는 구성과 로직을 공유하지만 위치와 현재 상태는 각자 가집니다.
- 인스턴스마다 다르게 둘 값은 변수의 Instance Editable을 켜서 레벨에서 조정합니다.
- 컴포넌트, 뷰포트, 이벤트 그래프, 디테일, 마이 블루프린트 패널은 각각 액터의 구성, 모습, 로직, 속성, 멤버를 다룹니다.
연습 문제
1. 문 B를 열었을 때 문 A는
같은 BP_Door 클래스로 문 A와 문 B를 배치했습니다. 플레이 중에 문 B 앞에서 상호작용해 B의 OpenDoor를 실행했습니다. 문 A도 함께 열릴까요? 클래스에서 OpenDoor 로직을 고친 경우와 비교해 설명해 보세요.
해설 보기
문 A는 열리지 않습니다. OpenDoor는 B라는 인스턴스에서 실행되었고, 그 실행이 바꾼 열림 상태는 B의 값입니다. A와 B는 같은 클래스에서 만들어졌을 뿐 서로의 상태를 바꾸는 연결이 없습니다.
반면 클래스에서 OpenDoor 로직 자체를 고치고 컴파일하면, 그 클래스로 배치한 A와 B 모두 다음 실행부터 고친 로직을 씁니다. 클래스는 공통 설계를, 인스턴스는 각자의 현재 값을 가진다는 차이입니다.
2. 문마다 다른 열림 각도
레벨의 정문은 90도, 창고 문은 45도만 열리게 하고 싶습니다. 문 블루프린트를 두 개 만들어야 할까요?
해설 보기
두 개를 만들 필요는 없습니다. BP_Door에 열림 각도를 나타내는 변수를 만들고 열리는 로직이 그 변수를 쓰게 한 뒤, 변수의 Instance Editable을 켭니다. 그러면 레벨에 놓인 정문과 창고 문을 각각 선택해 디테일 패널에서 90과 45로 따로 지정할 수 있습니다.
로직은 클래스 하나에서 공유하고, 인스턴스마다 다른 값만 레벨에서 바꾸는 방식이 재사용하기 쉽습니다.
3. 무엇이 액터일까
다음 가운데 액터로 만들 수 있는 것과 그렇지 않은 것을 나누어 보세요. 플레이어 캐릭터, 화면 위의 체력 바 위젯, 레벨에 놓인 보이지 않는 감지 영역, 월드를 나타내는 UWorld.
해설 보기
플레이어 캐릭터와 보이지 않는 감지 영역은 액터입니다. 감지 영역은 화면에 보이지 않지만 월드에 위치를 가지고 배치되므로 액터로 만들 수 있습니다.
체력 바 위젯은 화면에 그려지는 UI 오브젝트일 뿐 월드에 배치되는 액터가 아니고, UWorld는 액터들을 담는 월드 자체라서 액터가 아닙니다. 보이는지 여부가 아니라 월드에 놓이는 단위인지가 기준입니다.
블루프린트 클래스와 부모 클래스의 종류는 언리얼 엔진 공식 문서의 블루프린트 클래스 설명에서 확인할 수 있습니다.
다음 글 블루프린트 컴포넌트의 사용법에서는 액터에 기능을 더하는 컴포넌트를 추가하고, 계층을 정리하고, 그래프에서 다루는 방법을 살펴봅니다.