https://dev.epicgames.com/documentation/ko-kr/unreal-engine/sparse-class-data-in-unreal-engine?application_version=5.3

  • 자주 사용되는 Actor Type의 주복 데이터를 제거하여 Memory를 절약
  • 제공하는 기능
    • Memory에 Property의 사본을 단 하나만 존재하도록 유지
    • Designer가 BP Graph에서 Property 값에 Access하고 Class Default에서 편집 가능
  • 사용을 권장하는 경우
    • In-Game에서 동시에 여러 개의 Instance를 가지는 Actor Class의 Property
      • 상당한 Memory가 중복된 사본에 사용되고 있음을 의미한다.
    • 배치된 Actor Instance를 변경하지 않는 Property
      • Actor Instance가 값을 override하지 않아 EditInstanceOnly나 EditAnywhere 지정자가 필요하지 않는 경우
    • C++ 코드로 수정되지 않는 Property
      • 직접 Access하는 모든 C++ 코드는 접근자 함수에 대한 호출로 대체 되어야 한다.
  • Sparse Class Data를 사용하기 위해서는 Native C++ 코드가 필요하다.

예제

  • 더보기
    USTRUCT(BlueprintType)
    struct FMySparseClassData
    {
        GENERATED_BODY()
    
        FMySparseClassData() 
        : MyFloatProperty(0.f)
        , MyIntProperty(0)
        , MyNameProperty(NAME_None)
        { }
    
        // 에디터에서 이 프로퍼티의 디폴트값을 세팅할 수 있습니다.
        // 블루프린트 그래프에서는 액세스할 수 없습니다.
        UPROPERTY(EditDefaultsOnly)
        float MyFloatProperty;
    
        // 이 프로퍼티의 값은 C++ 코드로 설정됩니다.
        // 블루프린트 그래프에서 액세스할 수 있습니다(변경은 불가능함).
        UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
        int32 MyIntProperty;
    
        // 에디터에서 이 프로퍼티의 디폴트값을 세팅할 수 있습니다.
        // 블루프린트 그래프에서 액세스할 수 있습니다(변경은 불가능함).
        // 'GetByRef'는 블루프린트 그래프가 사본 대신 const 참조에 액세스함을 의미합니다.
        UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta=(GetByRef))
        FName MyNameProperty;
    };
    • EditAnywhere, EditInstanceOnly, BlueprintReadWrite가 Tag된 Property 대상으로는 작업할 수 없다
    • Sparse Class Data를 포함하는 Struct는 USTRUCT(BlueprintType) 지정자가 선언되어 있어야 한다.
      • 각 Property는 반드시 EditDefaultsOnly 지정자를 포함해야 한다.
      • 별도의 Category가 없는 경우, Default인 'My Sparse Class Data'를 받게 된다.
  • 더보기
    UCLASS(BlueprintType, SparseClassDataTypes = MySparseClassData)
    class AMyActor : public AActor
    {
        GENERATED_BODY()
    
    #if WITH_EDITOR
    public:
        // ~ 이 함수는 기존 데이터를 FMySparseClassData로 전송합니다.
        virtual void MoveDataToSparseClassDataStruct() const override;
    #endif // WITH_EDITOR
    
    #if WITH_EDITORONLY_DATA
        //~ 이 프로퍼티는 FMySparseClassData 구조체로 이동합니다.
    private:
        // 이 프로퍼티는 에디터에서 변경할 수 있지만, 클래스별로만 변경됩니다.
        // 블루프린트 그래프에서는 액세스하거나 변경할 수 없습니다.
        // 희소 클래스 데이터의 잠재적 후보입니다.
        UPROPERTY()
        float MyFloatProperty_DEPRECATED;
    
        // 이 프로퍼티는 에디터에서 변경할 수 없습니다.
        // 블루프린트 그래프에서 액세스할 수 있지만, 변경할 수는 없습니다.
        // 희소 클래스 데이터의 잠재적 후보입니다.
        UPROPERTY()
        int32 MyIntProperty_DEPRECATED;
    
        // 이 프로퍼티는 에디터에서 클래스별로 편집할 수 있습니다.
        // 블루프린트 그래프에서 액세스할 수 있지만, 변경할 수는 없습니다.
        // 희소 클래스 데이터의 잠재적 후보입니다.
        UPROPERTY()
        FName MyNameProperty_DEPRECATED;
    #endif // WITH_EDITORONLY_DATA
    
        //~ 나머지 프로퍼티는 인스턴스별로 변경할 수 있습니다.
        //~ 따라서 나머지 프로퍼티는 희소 클래스 데이터 구현에서 제외됩니다.
    public:
        // 이 프로퍼티는 배치된 MyActor 인스턴스에서 편집할 수 있습니다.
        // 희소 클래스 데이터의 잠재적 후보가 아닙니다.
        UPROPERTY(EditAnywhere)
        FVector MyVectorProperty;
    
        // 이 프로퍼티는 블루프린트 그래프에서 변경할 수 있습니다.
        // 희소 클래스 데이터의 잠재적 후보가 아닙니다.
        UPROPERTY(BlueprintReadWrite)
        FVector2D MyVector2DProperty;
    };
    • UCLASS(SparseClassDataTypes = [SPARSE_CLASS_DATA_TYPE])
      • 이 선언을 통해 이 Class에서 어느 Sparse Class Data를 사용할지 연결한다.
      • Class당 1개의 Sparse Class Data만 지정할 수 있다.
        • 여러개가 필요한 경우 이를 하나로 묶은 Wrapper를 Sparse Class Data로 지정해야 한다.
      • Sparse Class Data를 지정하면 UBT가 자동으로 Get[SPARSE_CLASS_DATA_TYPE] 함수를 생성해준다.
    • 다음은 기존 변수를 무난하게 Sparse Class Data로 변환하며 값을 설정하기 위한 세팅이다.
      • Sparce Class Data로 이동하는 Property 주변을 #if WITH_EDITORONLY_DATA Precomiler 처리
      • 원본 Class의 property의 변수명에 _DEPRECATED 접미사 추가
        • _DEPRECATED 접미사가 붙은 변수는 Unreal에서 인지하고 사용 시 warning을 발생시켜준다.
      • 원본 Class의 UPROPERTY에 모든 지정자를 제거하고 접근자를 private로 세팅
      • Editor Build(#if WITH_EDITOR) 환경에서 MoveDataToSparseClassDataStruct 함수 override
        • 원본 Class에서 Sparse Class Data로 1회성 복사를 수행해 기존 값을 보존
  • 더보기
    #include "Engine/BlueprintGeneratedClass.h"
    
    #if WITH_EDITOR
    void AMyActor::MoveDataToSparseClassDataStruct() const
    {
        // 이미 저장된 희소 데이터를 덮어쓰지 않도록 하세요.
        UBlueprintGeneratedClass* BPClass = Cast<UBlueprintGeneratedClass>(GetClass());
        if (BPClass == nullptr || BPClass->bIsSparseClassDataSerializable == true)
        {
            return;
        }
    
        Super::MoveDataToSparseClassDataStruct();
    
    
        #if WITH_EDITORONLY_DATA
        // 언리얼 헤더 툴(Unreal Header Tool, UHT)은 GetMySparseClassData를 자동 생성합니다.
        FMySparseClassData* SparseClassData = GetMySparseClassData();
    
        // 이 줄을 수정하여 모든 희소 클래스 데이터 프로퍼티를 포함합니다.
        SparseClassData->MyFloatProperty = MyFloatProperty_DEPRECATED;
        SparseClassData->MyIntProperty = MyIntProperty_DEPRECATED;
        SparseClassData->MyNameProperty = MyNameProperty_DEPRECATED;
        #endif // WITH_EDITORONLY_DATA
    }
    #endif // WITH_EDITOR
  • Sparse Class Data의 영향을 받은 Property를 Editor에서 편집/Access 하는 사용자는 차이를 느끼지 못한다.
  • Memory 사용량은 줄어든다.
  • Property가 C++에서 Reference되는 경우, Getter 함수 호출로 모두 대체 되어야 한다.
    • 예를 들어 SparceClassData::MyFloatPropertyData에 접근하려면 GetMyFloatProperty를 대신 호출해야 한다.
    • 다행히도 이러한 함수는 UHT가 자동으로 생성한다.
  • 해당 PROPERTY에 NoGetter MetaData 지정자를 선언하면 Getter 함수 생성을 방지할 수 있다.
  • 해당 PROPERTY에 값 대신 상수 참조로 Access 하고 싶은 경우 GetByRef MetaData 지정자를 사용해야 한다.

'UE5 > Utility' 카테고리의 다른 글

[Basic] Source Control  (0) 2024.05.09
[Basic] Packaging  (0) 2024.05.09
[Programming Tool] LLM(Low-level Memory Tracker)  (0) 2024.05.07

+ Recent posts