리플렉션 시스템 기초
UCLASS, UPROPERTY, UFUNCTION으로 런타임 타입 정보 활용하기
리플렉션 시스템 개요
런타임에 타입 정보에 접근하는 메커니즘
리플렉션(Reflection)은 프로그램이 실행 중에 자신의 구조(클래스, 프로퍼티, 함수 등)를 검사하고 수정할 수 있는 기능입니다. 언리얼 엔진은 UHT(Unreal Header Tool)를 통해 이를 구현합니다.
// 리플렉션 타입 계층
UField
└─ UStruct
├─ UClass // 클래스 메타데이터
├─ UScriptStruct // 구조체 메타데이터
└─ UFunction // 함수 메타데이터
└─ UEnum // 열거형 메타데이터
└─ FProperty // 프로퍼티 메타데이터
컴파일 전에 헤더 파일을 파싱하여 .generated.h 파일을 생성합니다. 이 파일에 리플렉션에 필요한 메타데이터가 포함됩니다.
UCLASS 지정자
클래스 레벨 메타데이터 설정
UCLASS(
Blueprintable, // BP에서 상속 가능
BlueprintType, // BP 변수 타입으로 사용 가능
Abstract, // 직접 인스턴스화 불가
NotBlueprintable, // BP 상속 금지
Transient, // 저장하지 않음
Config=Game, // Config 파일에서 설정 로드
DefaultToInstanced, // 인스턴스 기본값
EditInlineNew, // 프로퍼티 에디터에서 인라인 생성
meta=(DisplayName="My Class")
)
class MYGAME_API UMyClass : public UObject
{
GENERATED_BODY()
public:
// StaticClass()로 UClass 접근
// UMyClass::StaticClass()
};
클래스 정보 접근
// 클래스 정보 얻기
UClass* MyClassInfo = UMyClass::StaticClass();
UClass* InstanceClass = MyObject->GetClass();
// 클래스 이름
FString ClassName = MyClassInfo->GetName();
// 부모 클래스 확인
if (MyObject->IsA<ACharacter>())
{
// ACharacter 또는 그 자식 클래스
}
// 동적 캐스팅
ACharacter* Character = Cast<ACharacter>(MyActor);
if (Character)
{
// 캐스팅 성공
}
| 지정자 | 설명 |
|---|---|
| Blueprintable | 블루프린트에서 이 클래스를 상속할 수 있음 |
| BlueprintType | 블루프린트에서 변수 타입으로 사용 가능 |
| Abstract | 직접 인스턴스화 불가 (추상 클래스) |
| Transient | 디스크에 저장되지 않음 |
| Config=ConfigName | 지정된 Config 파일에서 설정 로드 |
| EditInlineNew | 프로퍼티 창에서 인라인으로 생성 가능 |
UPROPERTY 지정자
프로퍼티 직렬화, 에디터 노출, 네트워크 복제
에디터 노출
UCLASS()
class UMyClass : public UObject
{
GENERATED_BODY()
public:
// 모든 곳에서 편집 가능
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Stats")
float Health = 100.f;
// 기본값만 편집 (클래스 기본 오브젝트에서만)
UPROPERTY(EditDefaultsOnly)
int32 MaxHealth = 100;
// 인스턴스만 편집 (레벨에 배치된 액터)
UPROPERTY(EditInstanceOnly)
FString InstanceName;
// 읽기 전용 표시 (수정 불가)
UPROPERTY(VisibleAnywhere)
int32 CurrentLevel;
};
Blueprint 접근 및 네트워크
// Blueprint 접근 제어
UPROPERTY(BlueprintReadOnly)
float ReadOnlyValue;
UPROPERTY(BlueprintReadWrite)
float ReadWriteValue;
// 네트워크 복제
UPROPERTY(Replicated)
int32 ReplicatedValue;
UPROPERTY(ReplicatedUsing=OnRep_Health)
float NetworkedHealth;
UFUNCTION()
void OnRep_Health();
// 고급 지정자
UPROPERTY(Transient) // 저장하지 않음
float TemporaryValue;
UPROPERTY(SaveGame) // 세이브 게임에 포함
int32 SavedProgress;
UPROPERTY(meta=(ClampMin="0", ClampMax="100"))
int32 ClampedValue;
Edit 계열
- EditAnywhere - 어디서든 편집
- EditDefaultsOnly - 기본값만
- EditInstanceOnly - 인스턴스만
Visible 계열
- VisibleAnywhere - 어디서든 보기
- VisibleDefaultsOnly - 기본값만
- VisibleInstanceOnly - 인스턴스만
UFUNCTION 지정자
함수의 Blueprint 노출, 네트워크, 이벤트 처리
UCLASS()
class UMyClass : public UObject
{
GENERATED_BODY()
public:
// Blueprint에서 호출 가능
UFUNCTION(BlueprintCallable, Category="MyFunctions")
void CallableFunction();
// Pure 함수 - 상태 변경 없음, getter용
UFUNCTION(BlueprintPure, Category="MyFunctions")
float GetHealth() const;
// Blueprint에서 구현 (C++ 구현 없음)
UFUNCTION(BlueprintImplementableEvent)
void OnDamaged();
// C++ 기본 구현 + BP 오버라이드 가능
UFUNCTION(BlueprintNativeEvent)
void OnHealed();
void OnHealed_Implementation(); // 기본 구현
};
네트워크 UFUNCTION
// 서버에서 실행 (클라이언트 -> 서버 RPC)
UFUNCTION(Server, Reliable)
void ServerDoAction();
// 클라이언트에서 실행 (서버 -> 특정 클라이언트)
UFUNCTION(Client, Reliable)
void ClientReceiveData();
// 모든 클라이언트에서 실행 (서버 -> 모든 클라이언트)
UFUNCTION(NetMulticast, Unreliable)
void MulticastEffect();
// 콘솔 명령으로 실행
UFUNCTION(Exec)
void CheatGodMode();
// 에디터에서 버튼으로 호출
UFUNCTION(CallInEditor)
void TestFunction();
BlueprintCallable은 실행 핀이 있고 상태를 변경할 수 있습니다. BlueprintPure는 실행 핀이 없고 값만 반환하며, 매 프레임 호출될 수 있으니 주의하세요!
USTRUCT와 UENUM
구조체와 열거형의 리플렉션
USTRUCT(BlueprintType)
struct FCharacterStats
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Health = 100.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Mana = 50.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 Level = 1;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString Name;
};
UENUM(BlueprintType)
enum class ECharacterState : uint8
{
Idle UMETA(DisplayName="대기"),
Walking UMETA(DisplayName="걷기"),
Running UMETA(DisplayName="달리기"),
Attacking UMETA(DisplayName="공격"),
Dead UMETA(DisplayName="사망"),
MAX UMETA(Hidden)
};
// 사용 예시
UPROPERTY(EditAnywhere, BlueprintReadWrite)
ECharacterState CurrentState = ECharacterState::Idle;
DisplayName으로 에디터에 표시되는 이름을 지정하고, Hidden으로 에디터에서 숨길 수 있습니다.
핵심 요약
- 리플렉션 — UHT가 .generated.h 파일을 생성하여 런타임 타입 정보 제공
- UCLASS — Blueprintable, Abstract, Config 등으로 클래스 동작 정의
- UPROPERTY — EditAnywhere, BlueprintReadWrite, Replicated 등으로 프로퍼티 노출
- UFUNCTION — BlueprintCallable, Server/Client, BlueprintNativeEvent 등
- USTRUCT/UENUM — BlueprintType으로 블루프린트에서 사용 가능
도전 과제
배운 내용을 직접 실습해보세요
USTRUCT(BlueprintType)로 FCharacterStats 구조체를 만들고, UPROPERTY(EditAnywhere, BlueprintReadWrite)로 Strength, Dexterity, Intelligence, Vitality를 선언하세요. UENUM으로 ECharacterClass(Warrior, Mage, Archer)를 정의하세요.
RPG 캐릭터 클래스에 BlueprintCallable로 TakeDamage(), BlueprintPure로 GetCurrentHealth(), BlueprintNativeEvent로 OnLevelUp()을 구현하세요. CallInEditor로 테스트 함수도 추가하세요.
UClass::StaticClass()와 FProperty 이터레이터를 사용하여, 런타임에 캐릭터의 모든 UPROPERTY를 순회하며 값을 읽고 출력하는 디버그 유틸리티를 구현하세요. Cast<>와 IsA<>를 활용하여 타입 안전한 캐스팅을 연습하세요.