리플렉션 시스템
Blueprint (시각적 스크립팅)
Blueprint는 언리얼 엔진에서 제공하는 시각적 스크립팅 도구로 노드(블록)를 연결하여 게임 로직을 작성한다.
장점
- Blueprint 그래프 수정 후, 에디터에서 Play로 바로 확인 가능
- 노드를 연결해 로직을 작성하므로, 초보자도 쉽게 접근 가능
한계점
- 노드 수가 많아질수록 그래프가 복잡해져 가독성과 유지보수가 어려워짐
- 내부적인 추가 해석 과정을 거치기 때문에 고성능이 요구되는 시스템에서 병목 발생 가능성 존재
C++ (네이티브 코드)
장점
- 엔진 코어까지 직접 수정이 가능하고, 복잡하고 성능이 중요한 게임 로직을 빠르고 최적화된 방식으로 구현 가능
- 표준 라이브러리와 외부 라이브러리 사용이 자유로워, 대규모 프로젝트에 적합
- 포인터, 템플릿 같은 C++ 언어적 기능을 통해 메모리와 로직을 정교하게 다룰 수 있음
한계점
- C++ 코드를 수정하면 에디터를 재시작하거나 Live Coding을 다시 컴파일해야 하므로, 반복 작업에 불리함
Blueprint와 C++의 상호보완적 관계
실무에서는 둘을 함께 사용하는 경우가 많다. 하나만 사용하는 것보다 각각의 장점을 취하는게 일반적이다.
- Blueprint 활용 : UI 제작, 간단한 이벤트 처리, 시각적 연출 등 빠른 프로토타이핑과 직관적인 로직 작성에 사용
- C++ 활용 : 높은 성능이 필요한 게임플레이 로직이나 엔진 레벨의 확장, 복잡한 수학 연산 등에 사용
리플렉션(Reflection)이란?
언리얼 엔진의 리플렉션 시스템은 C++ 클래스의 변수 및 함수 정보를 엔진 내부의 메타데이터 형태로 저장하고, 이를 에디터나 블루프린트에서 활용할 수 있게 만들어주는 기술이다.
C++ 클래스에 있는 여러 멤버(변수, 함수 등)를 "Reflection(반사)"해, 에디터와 블루프린트에서 직접 설정, 호출이 가능하게 한다.
이 덕분에 프로그래머가 만든 C++ 로직의 뼈대를 디자이너나 다른 팀원들이 에디터에서 직관적으로 조정할 수 있다. 매개변수를 코드에서만 변경하는 것이 아니라, 에디터에서 바로 조정하여 반복 테스트를 빠르게 진행할 수 있다.
C++ 클래스 리플렉션 등록
리플렉션 관련 매크로
`.h`
#pragma once
...
#include "Item.generated.h"
UCLASS()
class SPARTAPROJECT_API AItem : public AActor
{
GENERATED_BODY()
public:
...
};
- `#include "Item.generated.h"`
- 언리얼 엔진이 자동 생성하는 헤더 파일로, 클래스의 리플렉션 및 엔진 통합에 필요한 코드가 들어있다.
- 반드시 헤더 파일의 `#include` 구문 마지막에 위치해야 한다.
- `UCLASS()`
- 언리얼 엔진의 리플렉션 시스템에 등록한다는 의미다.
- 이 매크로가 있어야 에디터에서 이 클래스를 인식하고 사용할 수 있다.
- `GENERATED_BODY()`
- 언리얼의 코드 생성 도구가 사용하는 코드를 삽입하는 역할을 한다.
- 클래스 내부에 필요한 리플렉션 정보를 자동으로 생성해 준다.
`UCLASS()` 매크로의 주요 지정자
`UCLASS()` 매크로는 클래스를 리플렉션 시스템에 등록하면서 몇 가지 옵션(지정자)을 설정할 수 있다. 필요에 따라 지정자들을 조합해 클래스가 어떻게 상호작용해야 할지 명시할 수 있다.
- 기본 동작
- 옵션을 주지 않으면, 블루프린트에서 상속이 가능하고 변수로 참조가 가능한 형태로 등록된다.
- 주요 옵션
- `Blueprintable` : 블루프린트에서 상속이 가능한 클래스로 만든다.
- `NotBlueprintable` : 블루프린트에서 상속할 수 없도록 만든다.
- `BlueprintType` : 블루프린트에서 변수나 참조로 사용할 수 있게 한다. 단, 이 옵션만 있으면 상속은 허용되지 않고 참조만 가능하다.
C++ 클래스를 상속 받은 Blueprint 클래스 생성
변수에 리플렉션 적용
`UPROPERTY()` 매크로의 주요 지정자
`UPROPERTY()`에는 여러 지정자를 작성해 자세한 설정을 할 수 있다.
- 편집 가능 범위
- `VisibleAnywhere` : 읽기 전용으로 표시. 수정 불가능.
- `EditAnywhere` : 클래스 기본값, 인스턴스 모두에서 수정 가능.
- `EditDefaultsOnly` : 클래스 기본값에서만 수정 가능.
- `EditInstanceOnly` : 인스턴스에서만 수정 가능.
- Blueprint 접근자
- `BlueprintReadWrite` : Blueprint 그래프에서 Getter / Setter 로 값을 읽거나 쓸 수 있다.
- `BlueprintReadOnly` : Blueprint 그래프에서 Getter 핀만 노출되어, 읽기만 가능하다.
- Category
- Details 패널에서 "Rotation" 범주(폴더) 아래에 표시된다.
- 여러 변수를 비슷한 카테고리에 묶으면, 세부 정보 패널에서 깔끔하게 정리되어 보인다.
- 메타 옵션
- `meta=(ClampMin="0.0")` : 에디터에서 변수 입력 시 최소값을 제한할 수 있다.
- `meta=(AllowPrivateAccess="true")` : 해당 멤버가 `private`로 선언되어 있어도, 에디터나 Blueprint에서 접근할 수 있도록 허용한다.
만약 `UPROPERTY()`만 있고, 추가 지정자가 하나도 없다면?
엔진 리플렉션 시스템에는 등록되지만, 에디터나 Blueprint에 노출되지 않는다. "엔진이 변수의 존재는 알지만, 외부에 보이지 않게 숨긴 상태"라고 볼 수 있다.
리플렉션에 등록만 되어 있어도 가비지 컬렉션과 직렬화 같은 엔진 내부 기능이 작동할 수 있다.
클래스 변수 리플렉션 등록
`.h`
...
UCLASS()
class SPARTAPROJECT_API AItem : public AActor
{
...
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item|Components")
USceneComponent* SceneRoot;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item|Components")
UStaticMeshComponent* StaticMeshComp;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Item|Properties")
float RotationSpeed;
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
};
`.cpp`
#include "Item.h"
AItem::AItem()
{
SceneRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
SetRootComponent(SceneRoot);
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
StaticMeshComp->SetupAttachment(SceneRoot);
PrimaryActorTick.bCanEverTick = true;
RotationSpeed = 90.0f;
}
void AItem::BeginPlay()
{
Super::BeginPlay();
}
void AItem::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (!FMath::IsNearlyZero(RotationSpeed))
{
AddActorLocalRotation(FRotator(0.0f, RotationSpeed * DeltaTime, 0.0f));
}
}
Blueprint에서 C++ 변수 조정
생성한 블루프린트에 들어가면 아래와 같은 창을 볼 수 있다. 이 창에서 값을 조절하면 빌드 없이 바로 적용된다.
함수에 리플렉션 적용
함수 리플렉션이란?
`UFUNCTION()` 매크로를 사용해 함수를 블루프린트에서 직접 호출할 수 있도록 등록할 수 있다.
`UFUNCTION()` 매크로의 주요 지정자
- Blueprint 관련
- `BlueprintCallable` : Blueprint 이벤트 그래프(노드)에서 호출(Execute) 가능한 함수로 만든다.
- `BlueprintPure` : Getter 역할만 수행한다.(Exec 핀 없이 Return Value만 노출)
- `BlueprintImplementableEvent` : 함수의 선언만 C++에 있고, 구현은 블루프린트에서 하도록 한다. C++ 코드에서는 함수 이름만 정의하고, 실제 동작은 Blueprint Event Graph 안에서 이벤트 노드처럼 구현된다.
만약 `UFUNCTION()`에 지정자를 하나도 쓰지 않으면?
`UPROPERTY()`와 마찬가지로, 언리얼 리플렉션에 등록되지만 노출되지 않는다.
"엔진이 함수의 존재는 알지만, Blueprint에서 호출할 수 없게 숨긴 상태"라고 보면 된다.
클래스 함수 리플렉션 등록
`.h`
...
UCLASS()
class SPARTAPROJECT_API AItem : public AActor
{
...
protected:
...
UFUNCTION(BlueprintCallable, Category = "Item|Actions")
void ResetActorPosition();
UFUNCTION(BlueprintPure, Category = "Item|Properties")
float GetRotationSpeed() const;
UFUNCTION(BlueprintImplementableEvent, Category = "Item|Event")
void OnItemPickedUp();
};
`.cpp`
...
void AItem::ResetActorPosition()
{
SetActorLocation(FVector::ZeroVector);
}
float AItem::GetRotationSpeed() const
{
return RotationSpeed;
}
Blueprint에서 함수 노드 사용
'본 캠프 > Note' 카테고리의 다른 글
[C++과 Unreal Engine으로 3D 게임 개발] 입력 매핑 구현 (0) | 2025.09.19 |
---|---|
[C++과 Unreal Engine으로 3D 게임 개발] 클래스를 활용한 캐릭터 구현 (0) | 2025.09.18 |
[C++과 Unreal Engine으로 3D 게임 개발] Tick 함수로 Transform 조정 (1) | 2025.09.17 |
[C++과 Unreal Engine으로 3D 게임 개발] Actor의 라이프 사이클 (0) | 2025.09.16 |
[C++과 Unreal Engine으로 3D 게임 개발] Actor 클래스에 컴포넌트 추가하기 (0) | 2025.09.15 |