Dev./UE 언리얼 엔진

[TIL_250127] [스파르타코딩클럽 후기] C++와 Unreal Engine으로 3D 게임 개발 - 리플렉션 시스템 이해하기!

raindrovvv 2025. 1. 27. 21:13

💭회고

설 연휴임에도 불구하고 공부를 했다😂😂

연휴라고 하면 보통 편하게 쉬기 마련이지만,
스파르타코딩클럽 언리얼 과정 중 [C++와 Unreal Engine으로 3D 게임 개발] 강의를 보며,
나름의 긴장감을 유지하면서 공부할 수 있었다!

집중이 잘되지 않았지만, 그래도 많은 내용을 공부할 수 있었다. 특히 리플렉션 시스템과 매크로에 대해 이해하는 데 많은 도움이 되었다. UCLASS(), UPROPERTY(), UFUNCTION() 매크로의 사용법을 정리하면서 에디터와 블루프린트, C++ 간의 상호작용을 보다 명확하게 이해하게 되었다.

언리얼 엔진의 리플렉션 시스템을 적극적으로 활용하면, 메쉬 설정, 머터리얼 설정 등을 보이게 처리해 작업 시 훨씬 편리해진다는 점을 알게 되었다.

스파르타코딩클럽 덕분에 설 연휴 동안에도 유익한 시간을 보낼 수 있었다! 앞으로도 더 많은 것을 배우고 성장해 나갈 생각이다🤗😁🤗

코드카타

🗺️마인드맵

📒학습 내용

UCLASS() 매크로

UCLASS() 매크로는 클래스를 리플렉션 시스템에 등록하고 추가적인 옵션(지정자)을 설정할 수 있다.

 

기본 동작

  • 옵션이 없을 경우, 블루프린트에서 상속 및 변수로 참조가 가능하게 등록
  • UCLASS() = UCLASS(Blueprintable, BlueprintType)

주요 태그

  • Blueprintable: 블루프린트에서 상속 가능.
  • NotBlueprintable: 블루프린트에서 상속 불가.
  • BlueprintType: 블루프린트에서 변수나 참조로 사용 가능 (상속 불가).

필요에 따라 지정자 조합으로 클래스와 블루프린트의 상호작용 방식을 명시할 수 있다.


UPROPERTY() 매크로

UPROPERTY()는 C++ 코드에서 변수에 메타데이터를 추가하고, 이를 언리얼 에디터와 엔진에 통합할 수 있게 하는 데 사용된다. 리플렉션 시스템(Reflection System)을 기반으로 하며, 변수의 특성을 정의하거나 에디터에서 노출할 때 중요한 역할을 한다. (일반 변수로는 할 수 없는 기능)

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

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

 

주요 태그

에디터 노출 관련

  • VisibleAnywhere: 에디터에서 읽기만 가능.
  • EditAnywhere: 에디터에서 값을 수정할 수 있음.
  • EditDefaultsOnly: 디폴트 값만 수정 가능.
  • EditInstanceOnly: 인스턴스 값만 수정 가능.
  • VisibleDefaultsOnly: 디폴트 값만 읽기 가능.

블루프린트 관련

  • BlueprintReadWrite: 블루프린트에서 읽기(Getter)/쓰기(Setter) 가능.
  • BlueprintReadOnly: 블루프린트에서 읽기만 가능.
  • BlueprintAssignable: 블루프린트에서 이벤트로 바인딩 가능.

카테고리 설정

  • 여러 변수를 비슷한 카테고리 안에 묶으면 디테일 패널에서 정리되어 보임.
  • Category="CategoryName": 에디터의 변수 그룹화를 위해 카테고리를 설정.

메모리 관리 및 성능

  • Transient: 런타임에서만 존재하며 저장되지 않음.
  • SaveGame: 이 변수는 저장될 수 있음.
  • Replicated: 네트워크로 복제됨 (멀티플레이 환경에서 사용).

메타 옵션 지정자

  • meta=(ClampMin="0.0"): 에디터에서 변수 입력 시 최소값을 제한.
  • meta=(AllowPrivateAccess="true"): 해당 멤버가 private로 선언되어 있어도, 에디터나 블루프린트에서 접근할 수 있도록 허용.

UFUNCTION() 매크로

Blueprint 관련 지정자

  • BlueprintCallable: Blueprint 이벤트 그래프(노드)에서 호출 가능한 함수로 만듦.
  • BlueprintPure: Getter 역할만 수행 (Exec 핀 없이 Return Value만 노출).
  • BlueprintImplementableEvent:
    • 함수의 선언만 C++에 있고, 구현은 블루프린트에서 함. C++ 코드에서는 함수 이름만 정의하고, 실제 동작은 Blueprint Event Graph 안에서 이벤트 노드처럼 구현됨.
    • C++에서 호출도 가능함.
지정자를 하나도 쓰지 않으면?
UPROPERTY()와 마찬가지로, 함수가 언리얼 리플렉션에 등록되지만, Blueprint에 특별히 노출되지 않음.
엔진이 함수의 존재는 파악하되, Blueprint에서 직접 호출할 수 없게 숨겨둔 상태라고 보면 됨.

리플렉션 시스템 이해하기

블루프린트를 사용하면 스테틱 메쉬 컴포넌트를 쉽게 추가하고 메쉬 모양도 클릭하여 설정할 수 있다. 반면에, C++로 액터를 구현할 때는 일일이 다 구현해야 해서 불편하다. 하지만 C++에서 리플렉션 시스템을 이용하면 메쉬 설정과 머터리얼 설정 등을 쉽게 처리할 수 있어서 작업이 훨씬 편리해진다.

 

#pragma once  
  
#include "CoreMinimal.h"  
#include "GameFramework/Actor.h"  
#include "Item.generated.h"  
DECLARE_LOG_CATEGORY_EXTERN(LogTeam, Warning, All); // (카테고리 이름, 로그 심각도 설정, 활성화)  
  
UCLASS()  
class START_API AItem : public AActor  
{  
    GENERATED_BODY()  
    public:   
    AItem();

1) C++ 클래스 리플렉션에 등록하기

  • #include "Item.generated.h":
    • 언리얼 엔진이 자동 생성하는 헤더 파일.
    • 리플렉션 및 엔진 통합에 필요한 코드 포함.
    • 반드시 헤더 파일의 마지막 #include 구문 아래에 위치!
  • UCLASS():
    • 클래스를 리플렉션 시스템에 등록.
    • 블루프린트 등 에디터에서 클래스 인식 및 사용 가능.
  • GENERATED_BODY():
    • 코드 생성 도구가 사용하는 코드 삽입.
    • 클래스 내부 리플렉션 정보 자동 생성.

 

2) C++로 만든 아이템 클래스

  • 기본 부모 클래스는 Item이 된다.
  • 블루프린트 클래스로 생성하면, C++로 작성된 속성이나 함수를 '시각적'으로 다룰 수 있게 된다.

 

클래스 디폴트 수정
인스턴스 수정

3) 변수에 리플렉션 시스템 적용하기

protected:  
    FVector Location;  
    FRotator Rotation;  
    FVector Scale;  
  
    float SpeedZ = 200.0f; // Z축 상하 이동 속도  
  
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Item|Property")  
    float RotationSpeed = 90.0f; // 회전 속도  
    float ScaleFreq = 2.0f; // 스케일 변경 2초 주기  
  
    float TimeElapsed = 0.0f;
    
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item|Components") // 상위 카테고리 Item, 하위 Components    
    USceneComponent* SceneRoot; 
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item|Components");
    UStaticMeshComponent* StaticMesh;
    
    UAudioComponent* AudioComponent;  
    virtual void BeginPlay() override;  
    virtual void Tick(float DeltaTime) override;

헤더 파일에서 리플렉션 시스템을 이용해, 에디터에서 수정할 수 있도록 EditAnywhere로 설정했으니 해당 부분을 지워도 된다.

: 왜? ➡️ 리플렉션 시스템 등록했으니, 에디터에서 수정할 수 있게 됐으니까!

 

📽️ 영상

 

4) UFUNCTION()함수에 리플렉션 시스템 적용하기

복잡한 C++ 로직을 블루프린트에서 간단한 노드로 불러와 제어할 수 있어서 작업 효율이 높아짐.

  • UPROPERTY()가 멤버 변수를 리플렉션 시스템에 등록한다면,
  • UFUNCTION()은 멤버 함수를 등록함!

.h

UFUNCTION(BlueprintCallable, Category = "Item|Action")  
void ResetActorPosition();  
  
UFUNCTION(BlueprintPure, Category = "Item|Property")  
float GetSpeed() const;  
  
UFUNCTION(BlueprintImplementableEvent, Category = "Item|Event")  
void PickedUP();

.cpp

void AItem::BeginPlay()  
{  
    Super::BeginPlay();  
    PickedUP();  
    // 초기 위치, 회전, 스케일 값 저장.  
    Location = GetActorLocation();  
    Rotation = GetActorRotation();  
    Scale = GetActorScale();  
}

// ~ 중략 ~

void AItem::ResetActorPosition()  
{  
    SetActorLocation(FVector::ZeroVector);  
}  
  
float AItem::GetRotationSpeed() const  
{  
    return RotationSpeed;  
}
`FVector::ZeroVector` : 원점으로.

 

➡️ 블루프린트에서 아래와 같이 함수를 찾을 수 있다.


🟣오늘의 옵시디언 현황