Unreal

언리얼 엔진 리플렉션

(ꐦ •᷄ࡇ•᷅) 2025. 1. 23. 12:31

1. 리플렉션 시스템 이해하기

1 - 1. Blueprint(시각적 스크립팅) 이해

  • Blueprint는 언리얼 엔진에서 제공하는 시각적 스크립팅 도구로, 노드(블록)을 연결하여 게임 로직을 작성한다.
  • 장점
  • Blueprint 그래프를 수정 후, 에디터에서 Play 버튼을 누르면 곧바로 결과를 확인할 수 있다.
  • 아이디어를 빠르게 검증하고 반복할 때 큰 장점이 된다.
  • 레고 블록을 쌓듯이 노드를 연결해 로직을 작성하므로, 프로그래밍 언어에 익숙하지 않은 초급자들도 쉽게 접근할 수 있다.

한계점

  • 노드 수가 많아질수록 그래프가 복잡해져, 가독성과 유지보수가 어려워질 수 있다.
  • C++과 비교했을 때, Blueprint는 내부적으로 추가 해석 과정을 거친다. 따라서 물리 연산이나 AI 같이 높은 성능이 필요한 시스템에서는 병목이 될 수 있다.

 

1 - 2. C++(네이티브 코드 프로그래밍) 이해

장점

  • 엔진 코어까지 직접 수정 가능하며, 복잡하고 성능이 중요한 게임 로직을 빠르고 최적화된 방식으로 구현할 수 있다.
  • 표준 라이브러리와 외부 라이브러리를 자유롭게 사용할 수 있어, 대규모 프로젝트에 적합하다.
  • 포인터, 템플릿 같은 C++ 언어적 기능을 통해 메모리와 로직을 정교하게 다룰 수 있다.

한계점

  • C++ 코드를 수정하면 에디터를 재시작하거나 Live Coding을 다시 컴파일해야 하므로, 반복 작업 시 조금 번거로울 수 있다.

 

1 - 3. Blueprint와  C++의 상호보완적 관계

실무에서는 Blueprint와 C++을 함께 사용하는 경우가 많다. 하나만 사용하는 것보다 각각의 장점을 취하는 하이브리드 워크플로우가 일반적이다.

 

Blueprint 활용

  • UI 제작
  • 간단한 이벤트 처리
  • 시각적 연출 등
  • 빠른 프로토타이핑과 직관적인 로직 작성에 사용한다.

 

C++ 활용

  • 높은 성능이 필요한 게임 플레이 로직
  • 엔진 레벨의 확장
  • 복잡한 수학 연산 등

 

이렇게 분업하면 개발 속도와 퍼포먼스 모두 확보하기가 수월해진다.

 

1 - 4. 리플렉션(Reflection)이란?

  • 언리얼 엔진의 리플렉션 시스템은 C++ 클래스의 변수 및 함수 정보를 엔진 내부의 메타 데이터 형태로 저장하고, 이를 에디터나 블루프린트에서 활용할 수 있게 만들어주는 기술이다.
  • C++ 클래스에 있는 여러 멤버(변수, 함수 등)를 리플렉션해 에디터와 블루프린트에서 직접 설정, 호출이 가능하도록 한다.
  • 이 덕분에 프로그래머가 만든 C++ 로직의 뼈대를 디자이너나 다른 팀원들이 에디터에서 직관적으로 조정할 수 있다.
  • 매개변수를 코드에서만 변경하는 것이 아니라, 에디터에서 바로 조정(슬라이더나 숫자 입력)하여 반복 테스트를 빠르게 진행할 수 있다.
  • 리플렉션 시스템을 제대로 이해하고 활용하면, 큰 프로젝트에서도 개발 효율과 협업 효과를 극대화할 수 있다.

2. C++ 클래스 리플렉션에 등록하기

2 - 1 클래스 분석

#include "Item.generated.h"

 

#include "Item.generated.h"

  • 클래스를 만들면 "클래스이름.generated.h"이 클래스의 헤더 파일에 include 된 것을 볼 수 있다.
  • 이것은 언리얼 엔진이 자동 생성하는 헤더 파일로, 클래스의 리플렉션 및 엔진 통합에 필요한 코드들이 들어가 있다.
  • 반드시 헤더 파일의 가장 마지막 #include 구문 아래에 위치해야 한다. 다른 #include보다 아래에 오지 않으면 빌드 에러 발생 위험이 있다.
UCLASS()
class PROJECTJS_API AItem : public AActor
{
	GENERATED_BODY()
	
public:	
	AItem();
};

 

UCLASS()

  • 해당 클래스를 언리얼 엔진의 리플렉션 시스템에 등록한다는 의미이다.
  • 이 매크로가 있어야만 블루 프린트 등 에디터 차원에서 이 클래스를 인식하고 사용할 수 있다.

 

GENERATED_BODY()

  • 언리얼의 코드 생성 도구가 사용하는 코드를 삽입하는 역할을 한다.
  • 클래스 내부에 필요한 리플렉션 정보를 자동으로 생성해 준다.

 

2 - 2 UCLASS() 매크로의 주요 지정자

  • UCLASS() 매크로는 클래스를 리플렉션 시스템에 등록하면서, 추가적으로 몇 가지 옵션(지정자)을 설정할 수 있다.
  • 기본 동작
  • 만약 UCLASS()에 옵션을 주지 않으면, 블루 프린트에서 상속이 가능하고, 변수로 참조가 가능한 형태로 등록된다. 
  • 즉, 내부적으로 Blupritable, BlueprintType과 동일한 효과를 가지게 된다.

주요 옵션

Blueprintable

  • 블루 프린트에서 상속 가능한 클래스로 만든다.

NotBlueprintable

  • 블루 프린트에서 이 클래스를 상속할 수 없도록 한다.

BlueprintType

  • 블루 프린트에서 변수나 참조로 사용할 수 있게 한다.
  • 이 옵션만 있으면 상속은 허용되지 않고 참조만 가능하다.

 

필요에 따라 이 지정자들을 조합해 클래스가 어떻게 블루프린트와 상호작용해야 할지 명시할 수 있다.

 

2 - 3. C++ 클래스 상속받은  Blueprint 클래스 생성

 

클래스를 우클릭해 이 클래스를 상속하는 블루프린트를 만들면 된다.

이 블루프린트 안에서 C++로 작성된 속성이나 함수를 시각적으로 다룰 수 있다.


3. 변수에 리플렉션 적용하기

3 - 1. UPROPERTY() 매크로의 주요 지정자

UPROPERTY()에는 여러 지정자를 작성해 에디터에서의 표시 여부나 블루 프린트 접근성, 읽기/쓰기 권한 등을 자세하게 설정할 수 있다. 

 

1. 편집 가능 범위 지정자

  • VisibleAnywhere: 읽기 전용으로 표시되며, 수정은 불가능
  • EditAnywhere: 클래스 기본값, 인스턴스 모두에서 수정 가능
  • EditDefaultsOnly: 클래스 기본값에서만 수정 가능
  • EditInstanceOnly: 인스턴스에서만 수정 가능

2. Blueprint 접근성 지정자

  • BlueprintReadWrite: 블루프린트 그래프에서 Getter/Setter로 값을 읽거나 쓸 수 있다.
  • BlueprintReadOnly: 블루프린트 그래프에서 Getter 핀만 노출되어 읽기만 가능하다.

3. Category 지정자

  • Details 패널에서 이 변수는 해당 카테고리의 범주 아래 표시된다.
  • 여러 변수를 비슷한 카테고리에 묶으면, 세부 정보 패널에서 깔끔하게 정리되어 보인다.

4. 메타 옵션 지정자

meta=(ClampMin="0.0")

 

에디터에서 변수 입력 시 최소값을 제한할 수 있다.

 

meta=(AllowPrivateAccess="true")

 

해당 멤버가 private로 선언되어 있어도, 에디터나 블루프린트에서 접근할 수 있도록 허용한다.

 

5. 만약 UPROPERTY()만 있고 추가 지정자를 하나도 주지 않는다면?

  • 엔진 리플렉션 시스템에는 등록되어 있지만, 에디터나 블루프린트에 노출되지는 않는다.
  • 엔진이 변수의 존재는 알고 있지만 외부에서는 보이지 않게 숨겨둔 상태이다.
  • 리플렉션에 등록만 되어 있어도 가비지 컬렉션(메모리 관리)과 직렬화(세이브/로드) 같은 엔진 내부 기능이 작동할 수 있다.

4. 함수 리플렉션

4 - 1. 함수 리플렉션이란?

  • 함수 또한 블루프린트에서 직접 호출할 수 있도록 등록할 수 있다. 이렇게 하면 복잡한 C++ 로직을 블루프린트에서 간단한 노드로 불러와 제어할 수 있으므로 작업 효율이 높아진다.
  • UPRPERTY()가 멤버 변수를 리플렉션 시스템에 등록한다면 UFUNCTION()은 멤버 함수를 등록한다.
  • 즉, C++에서 만든 함수를 블루프린트 노드로 노출하고 싶을 땨, UFUNCTION() 매크로를 사용한다.

4 - 2. UFUNCTION() 매크로의 주요 지정자

블루프린트 관련 지정자

BlueprintCallable

  • 블루프린트 이벤트 그래프에서 호출 가능한 함수로 만든다.

BlueprintPure

  • Getter 역할만 수행한다.

BlueprintImplementableEvent

  • 함수의 선언만 C++에 있고 구현은 블루프린트에서 하도록 한다.

4 - 3. 만약 UFUNCTION()에 지정자를 하나도 쓰지 않았다면?

  • UPROPERTY()와 마찬가지로 함수가 언리얼 리플렉션에 등록되긴 하지만, 특별히 블루프린트에 노출되지는 않는다.
  • 엔진이 함수의 존재는 파악하되, 블루프린트에서 직접 호출할 수 없게 숨겨둔 상태이다. 

5. 예시

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Item.generated.h"

UCLASS()
class PROJECTJS_API AItem : public AActor
{
	GENERATED_BODY()
	
public:	
	AItem();

protected:
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item|Components")
	USceneComponent* SceneRoot;
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Components")
	UStaticMeshComponent* StaticMeshComp;
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Components")
	UAudioComponent* AudioComp;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Item|Properties")
	float RotationSpeed;

protected:
	virtual void BeginPlay() override;
	virtual void Tick(float DeltaTime) override;

	UFUNCTION(BlueprintCallable, Category = "Item|Actions")
	void ResetActorPosition();

	UFUNCTION(BlueprintPure, Category = "Item|Properties")
	float GetRotationSpeed() const;

	UFUNCTION(BlueprintImplementableEvent, Category = "Item|Events")
	void OnItemPickUp();
};