데이터에 특화된 자료 형태 커스텀 변수 타입을 생성하여 프로젝트를 체계적으로 관리할 수 있다.
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));
}
}