티스토리 뷰

언리얼엔진

언리얼엔진 CDO 란

silbaram 2025. 4. 3. 20:14
728x90

언리얼 엔진 오브젝트 시스템 개요

언리얼 엔진은 C++ 위에 자체적인 오브젝트 관리 시스템(UObject System)을 구축했습니다. 이 시스템은 리플렉션(Reflection), 가비지 컬렉션(Garbage Collection), 직렬화(Serialization), 에디터 통합 등 다양한 기능을 제공합니다. UClass와 CDO는 이 시스템의 핵심적인 부분입니다.

UClass 란 무엇인가?

UClass런타임(Runtime)에서 특정 C++ 클래스에 대한 정보를 담고 있는 UObject입니다. 쉽게 말해, 게임이나 에디터가 실행되는 동안 해당 클래스의 "설명서" 또는 "메타데이터" 역할을 합니다.

  • 역할: 클래스의 이름, 부모 클래스, 해당 클래스가 포함하는 프로퍼티(UPROPERTY로 선언된 변수들), 함수(UFUNCTION으로 선언된 함수들), 클래스 플래그 등의 정보를 가지고 있습니다.
  • 생성: Unreal Header Tool (UHT)이 GENERATED_BODY() 매크로와 UCLASS() 매크로가 포함된 헤더 파일을 파싱하여 각 클래스에 대한 UClass 정보를 생성하는 코드를 자동으로 만들어냅니다.
  • 용도:
    • 리플렉션: 런타임에 클래스의 구조(프로퍼티, 함수 등)를 파악하고 접근하는 데 사용됩니다. 이는 블루프린트 시스템, 에디터 디테일 패널, 네트워크 리플리케이션 등에서 광범위하게 활용됩니다.
    • 객체 생성: 특정 클래스의 새 인스턴스를 생성할 때(NewObject, SpawnActor 등) 어떤 클래스를 만들지 지정하는 데 사용됩니다.
    • 타입 검사: IsA<T>() 와 같은 함수를 통해 객체가 특정 클래스 또는 그 자식 클래스의 인스턴스인지 확인할 때 사용됩니다.

CDO (Class Default Object) 란 무엇인가?

CDO는 Class Default Object(클래스 기본 오브젝트)의 약자입니다.

  • 정의: 모든 UClass는 자신과 연결된 단 하나의 CDO 인스턴스를 가집니다. 이 CDO는 해당 클래스가 로드될 때(엔진/에디터 시작 시 또는 모듈 로드 시) 자동으로 생성되는 실제 객체 인스턴스입니다.
  • 역할: 해당 클래스의 기본값(Default Values)을 저장하는 템플릿 역할을 합니다. 클래스의 C++ 생성자에서 초기화되거나, 헤더에서 UPROPERTY에 직접 할당된 초기값들이 CDO에 반영됩니다.
  • 생성: UClass가 메모리에 로드될 때 엔진에 의해 자동으로 생성되며, 해당 클래스의 생성자를 호출하여 초기화됩니다.
  • 용도:
    • 기본값 제공: NewObject<T>() 또는 SpawnActor<T>() 등을 사용하여 해당 클래스의 새 인스턴스를 생성할 때, 새로 생성된 객체의 프로퍼티들은 CDO의 프로퍼티 값으로 초기화됩니다. 즉, CDO는 객체 생성 시 "기본 상태"를 제공하는 원본 역할을 합니다.
    • 템플릿/원형(Archetype): 블루프린트 클래스는 C++ 클래스로부터 파생되는데, 블루프린트에서 설정하는 기본값들은 해당 블루프린트의 CDO(정확히는 GeneratedClass의 CDO)에 저장됩니다.
    • 성능: 런타임에 클래스의 기본 프로퍼티 값을 알아내야 할 때, 리플렉션 정보를 매번 파싱하는 것보다 이미 생성된 CDO 인스턴스에서 값을 읽어오는 것이 더 빠릅니다.
    • 에디터 통합: 에디터의 디테일 패널에서 클래스의 기본값을 보거나 수정할 때 내부적으로 CDO (또는 블루프린트의 경우 해당 GeneratedClass의 CDO)와 상호작용합니다.

UClass와 CDO의 관계

  • UClass는 클래스에 대한 설명/메타데이터입니다. (예: "이 클래스에는 'Health'라는 이름의 float 변수가 있다.")
  • CDO는 해당 클래스의 실제 인스턴스이며, 모든 프로퍼티의 기본값을 가지고 있습니다. (예: "이 클래스의 'Health' 변수 기본값은 100.0f 이다.")
  • 모든 UClass 객체는 내부적으로 자신의 CDO를 가리키는 포인터를 가지고 있습니다.

코드 예제

아래는 UClass와 CDO를 사용하는 간단한 예제입니다.

1. 예제 클래스 정의 (MyObject.h)

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "MyObject.generated.h"

UCLASS(Blueprintable, BlueprintType) // UCLASS 매크로를 통해 UObject 시스템에 등록
class YOURPROJECTNAME_API UMyObject : public UObject
{
    GENERATED_BODY() // UHT가 필요한 코드를 생성하도록 함

public:
    // 생성자에서 기본값 설정
    UMyObject();

    // UPROPERTY 매크로를 사용하여 리플렉션 시스템에 등록하고 기본값 설정
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "My Object Properties")
    int32 MyIntProperty;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "My Object Properties")
    FString MyStringProperty;

    UPROPERTY(VisibleAnywhere, Category = "My Object Properties")
    float MyFloatProperty_NoInit; // 생성자에서 초기화할 프로퍼티
};

2. 예제 클래스 구현 (MyObject.cpp)

#include "MyObject.h"

UMyObject::UMyObject()
{
    // 생성자에서 프로퍼티 기본값 설정
    MyIntProperty = 10;
    MyStringProperty = TEXT("DefaultString");
    MyFloatProperty_NoInit = 99.9f; // 여기서 설정된 값이 CDO에 저장됨
}

3. UClass 및 CDO 사용 예제 (예: 다른 Actor 클래스 내부)

#include "MyObject.h" // 위에서 정의한 클래스 포함
#include "Engine/Engine.h" // GEngine 사용을 위해

// ... Actor 클래스의 함수 내부 ...

void AMyActor::SomeFunction()
{
    // 방법 1: StaticClass()를 사용하여 UClass 얻기 (컴파일 타임에 클래스 타입을 알 때)
    UClass* MyObjectClass = UMyObject::StaticClass();

    if (MyObjectClass)
    {
        UE_LOG(LogTemp, Warning, TEXT("UMyObject의 UClass 이름: %s"), *MyObjectClass->GetName());

        // UClass로부터 CDO 얻기 (템플릿 인자로 클래스 타입 명시)
        UMyObject* DefaultObject = MyObjectClass->GetDefaultObject<UMyObject>();
        // 또는 UObject* DefaultObjectRaw = MyObjectClass->GetDefaultObject();

        if (DefaultObject)
        {
            UE_LOG(LogTemp, Warning, TEXT("CDO의 MyIntProperty 기본값: %d"), DefaultObject->MyIntProperty);
            UE_LOG(LogTemp, Warning, TEXT("CDO의 MyStringProperty 기본값: %s"), *DefaultObject->MyStringProperty);
            UE_LOG(LogTemp, Warning, TEXT("CDO의 MyFloatProperty_NoInit 기본값: %f"), DefaultObject->MyFloatProperty_NoInit);

            // 주의: CDO의 값을 직접 수정하는 것은 일반적으로 권장되지 않습니다.
            // CDO는 템플릿이므로, 런타임에 CDO를 변경하면 이후 생성되는 모든 객체에 영향을 줄 수 있습니다.
            // DefaultObject->MyIntProperty = 100; // 이런 코드는 피하는 것이 좋습니다.
        }

        // 새 객체 인스턴스 생성
        UMyObject* NewMyObjectInstance = NewObject<UMyObject>(this, MyObjectClass); // this는 Outer 객체 지정

        if (NewMyObjectInstance)
        {
            // 새로 생성된 객체의 프로퍼티 값은 CDO의 값으로 초기화됩니다.
            UE_LOG(LogTemp, Warning, TEXT("새 인스턴스의 MyIntProperty 초기값: %d"), NewMyObjectInstance->MyIntProperty); // 출력: 10
            UE_LOG(LogTemp, Warning, TEXT("새 인스턴스의 MyStringProperty 초기값: %s"), *NewMyObjectInstance->MyStringProperty); // 출력: DefaultString

            // 새 인스턴스의 값은 자유롭게 수정 가능
            NewMyObjectInstance->MyIntProperty = 500;
            UE_LOG(LogTemp, Warning, TEXT("새 인스턴스의 MyIntProperty 수정 후 값: %d"), NewMyObjectInstance->MyIntProperty); // 출력: 500
        }
    }

    // 방법 2: 기존 객체 인스턴스로부터 UClass 얻기
    UMyObject* ExistingObject = NewObject<UMyObject>(); // 예시로 객체 하나 생성
    if (ExistingObject)
    {
        UClass* ClassFromInstance = ExistingObject->GetClass();
        if (ClassFromInstance)
        {
             UE_LOG(LogTemp, Warning, TEXT("기존 객체로부터 얻은 UClass 이름: %s"), *ClassFromInstance->GetName());

             // 마찬가지로 CDO 접근 가능
             UMyObject* CDOFromInstanceClass = ClassFromInstance->GetDefaultObject<UMyObject>();
             if (CDOFromInstanceClass)
             {
                 UE_LOG(LogTemp, Warning, TEXT("기존 객체의 클래스 CDO의 MyIntProperty: %d"), CDOFromInstanceClass->MyIntProperty);
             }
        }
        // ExistingObject->ConditionalBeginDestroy(); // 예시로 생성한 객체 정리 (GC가 처리하도록 둠)
    }
}

예제 설명:

  1. UMyObject 클래스를 UCLASS() 매크로와 함께 정의하여 언리얼 오브젝트 시스템의 일부로 만듭니다.
  2. UPROPERTY() 매크로를 사용하여 멤버 변수들을 리플렉션 시스템에 노출시키고, 일부는 생성자에서 기본값을 설정합니다.
  3. 다른 클래스(AMyActor::SomeFunction) 내에서:
    • UMyObject::StaticClass()를 호출하여 UMyObjectUClass 객체를 얻습니다.
    • MyObjectClass->GetDefaultObject<UMyObject>()를 호출하여 해당 UClass에 연결된 CDO를 얻습니다.
    • CDO의 프로퍼티 값을 읽어 기본값을 확인합니다. 이 값들은 UMyObject 생성자에서 설정한 값과 동일합니다.
    • NewObject<UMyObject>()를 사용하여 UMyObject의 새 인스턴스를 생성합니다. 이 때, 새 인스턴스의 프로퍼티들은 CDO의 값으로 자동 초기화됩니다.
    • 새 인스턴스의 프로퍼티 값은 이후 자유롭게 변경할 수 있습니다.
    • 기존 객체의 GetClass() 멤버 함수를 통해서도 해당 객체의 UClass를 얻을 수 있습니다.

요약

  • UClass: 클래스의 런타임 메타데이터(정보) 객체. 리플렉션, 객체 생성, 타입 검사에 사용됩니다.
  • CDO: 클래스의 기본값을 담고 있는 실제 인스턴스. 각 UClass마다 하나씩 존재하며, 새 객체를 생성할 때 초기값을 제공하는 템플릿 역할을 합니다.

UClass와 CDO는 언리얼 엔진의 강력한 기능들(블루프린트, 에디터, 네트워크 등)을 가능하게 하는 핵심 요소입니다. 이 개념을 이해하면 언리얼 엔진의 작동 방식을 더 깊이 이해하는 데 도움이 될 것입니다.

728x90
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함