메모내용

Struct

리플렉션에 관련된 언리얼 오브젝트의 계층 구조

언리얼 구조체

데이터에 특화된 자료 형태 커스텀 변수 타입을 생성하여 프로젝트를 체계적으로 관리할 수 있다.

구조체 구현하기

                
                    USTRUCT(BlueprintType) // 언리얼 엔진 블루프린트와 호환되는 성격을 가진다
                    struct FMyStruct
                    {
                        GENERATED_BODY() // 언리얼 엔진 리플렉션 시스템,직렬화 기능 제공,

                        // 생성자 1
                        FMyStruct(){
                            
                        }

                        // 생성자 2
                        // 언리얼 오브젝트 가 아니니까, NewObject API 를 통해 생성하는것이 아니니까 
                        // 인수를 가지는 생성자도 선언이 가능하다
                        FMyStruct(int32 a, int32 b, UObject* Obj)
                            : MyIntegerMemberVariable(a)
                            , NativeOnlyMemberVariable(b)
                            , SafeObjectPointer(Obj)
                        {

                        }

                        /* 이 멤버 변수는 블루프린트 그래프를 통해 액세스 할 수 있습니다. */
                        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="TEST")
                        int32 MyIntegerMemberVariable;

                        /* 이 멤버 변수는 블루프린트 그래프를 통해 엑세스 할 수 없습니다. */
                        int32 NativeOnlyMemberVariable;

                        /* 언리얼 오브젝트를 가리키는 포인터를 선언한다면, UPROPERTY 를 붙여줘야 자동으로 메모리 관리를 한다
                           반드시 넣어준다
                           이 UObject 포인터는 블루프린트 그래프에서 액세스 할 수 없으나 (BlueprintReadWrite 등 지정자가 없으니)
                           UE4 의 리플렉션, 스마트 포인터, 가비지 컬렉션 시스템에 표시 됩니다.

                           반대로,
                           값 타입으로서
                           메모리를 관리할 필요가 없다면, 리플렉션으로 조회할 필요가 없는 값이라면
                           UPROPERTY 를 붙일 필요가 없다.
                        */
                        UPROPERTY()
                        UObject* SafeObjectPointer;
                    }
                
            

구조체 추가 정보

예시

                
                    // GameInstance.cpp
                    #include "GameInstance.h"
                    #include "Algo/Accumulate.h"

                    // 헤더파일에서 선언됨 (메모 편의상 cpp 에 적음)
                    USTRUCT()
                    struct FStudentData
                    {
                        GENERATED_BODY()

                        UPROPERTY()
                        FString Name; // 이름

                        UPROPERTY()
                        int32 Order; // 순번

                        // Hash 를 사용하는 컨테이너의 Key 값이 되기위한 조건
                        
                        // 조건 1. == 연산자 오버로딩 구현
                        bool operator==(const FStudentData& InOther)
                        {
                            return Order == InOther.Order;
                        }

                        // 조건 2. GetTypeHash 함수 구현
                        friend FORCEINLINE int32 GetTypeHash(const FStudentData& InStudentData)
                        {
                            return GetTypeHash(InStudentData.Order); // int32 Order 의 해시값을 사용하도록 한다.
                        }

                    };

                    // 헤더파일에 클래스 내부에 선언된 멤버변수 ( 메모 편의상 cpp 에 적음 )
                    // 값타입이며, 데이터만 관리하고, 리플렉션 기능을 사용하지 않을것이기 때문에 UPROPERTY 를 쓰지 않아도 된다.
                    TArray<FStudent> StudentData;

                    // cpp 에서 함수 선언, 여기서만 쓸것이기 때문에
                    FString MakeRandomName()
                    {
                        TCHAR FirstChar[] = TEXT("김이박최");
                        TCHAR MiddleChar[] = TEXT("상해지성");
                        TCHAR LastChar[] = TEXT("수은원연");

                        TArray<TChar> RandomArr;
                        RandomArray.SetNum(3); // 3공간 할당
                        RandomArr[0] = FirstChar[FName::RandomRange(0,3)];
                        RandomArr[1] = MiddleChar[FName::RandomRange(0,3)];
                        RandomArr[2] = LastChar[FName::RandomRange(0,3)];

                        return RandomArr.GetData(); // TChar[] 이 반환되고, 이는 FString 으로 변환되어 반환된다.
                    }

                    void GameInstance::Test()
                    {

                        const int32 StudentNum = 300;
                        for (int32 i = 0; i <StudentNum ; i++)
                        {
                            // 구조체 복사 비용이 생길것이기 때문에 Add 보단 Emplace 가 좀더 효과적
                            StudentData.Emplace(FStudentData(MakeRandomName(),i));
                        }

                        // 1. TArray 에다가 옮기기 (중복 허용)
                        TArray<FString> AllStudentNames;
                        // Algo:: Transform  데이터를 옮기는데 유용한 함수
                        // 첫번째 인수로부터 값을 추출해 두번째 인수로 옮기는데, 람다함수를 받아 어떤값을 옮길것인지 적어줄 수 있다.
                        // 마치 JavaScript 의 Array.map 과 유사하다.
                        Algo::Transform(StudentData, AllStudentNames, [](const FStudentData& Val){
                            return Val.Name;
                        });

                        UE_LOG(LogTemp,Log,TEXT("모든 학생 이름 수 : %d"), AllStudentName.Num()); // 300 개

                        // 2. TSet 에다 옮기기 (중복 불가)
                        TSet<FString> AllStudentNames_unique;
                        Algo::Transform(StudentData, AllStudentNames_unique, [](const FStudentData& Val){
                            return Val.Name;
                        });

                        UE_LOG(LogTemp,Log,TEXT("모든 학생 이름 수 : %d"), AllStudentName.Num()); // 64 개



                        // 3-1. TMap 에다 옮기기 (중복시 덮어쓰기)
                        TMap<FString, int32> StudentMapByName;
                        Algo::Transform(StudentData,StudentMapByName, [](const FStudentData& Val){
                            return TPair<FString, int32>(Val.Name, Val.Order)
                        } );
                        // 63종의 이름을 키로 가진 TMap 이 생성된다.

                        // 3-2. 중복이 가능한 TMultiMap 에다 할당 (중복 허용)
                        TMulitiMap<FString, int32> StudentMultiMapByName;
                        Algo::Transform(StudentData, StudentMultiMapByName, [](const FStudentData& Val){
                            return TPair<FString,int32>(Val.Name, Val.Order);
                        });
                        // 300 개의 학생 이름을 키로 가진 TMultiMap 으로 설정된다.

                        // 3-3. 찾기
                        const FString TargetName(TEXT("이혜은"));
                        TArray<int32> AllOrders; // 이혜은이라는 이름을 가진 학생들의 Order 배열 저장소
                        StudentMultiMapByName.MultiFind(TargetName, AllOrders); // AllOrders 에다가 저장


                        // 4. Hash 를 사용하는 컨테이너에, 커스텀 자료형 을 사용하기
                        // operator== 와 int32 GetTypeHash() 함수가 구현되어 있어야 합니다. 구현되어 있지 않다면 에러 발생합니다.
                        TSet<FStudentData> StudentSet;
                        for(int32 idx = 1; idx <= SutdentNum ; ++idx)
                        {
                            StudentData.Emplace(FStudentData(MakeRandomName(), idx));
                        }

                    }