Unreal Struct
- 관련 프로퍼티를 체계화 및 조작할 수 있는 데이터 구조체이다.
- 구조체를 통해 커스텀 변수 타입을 생성해서 프로젝트를 체계화할 수 있다.
- 데이터 저장/전송에 특화된 가벼운 객체이다.
- Struct는 단순한 데이터 타입에 적합하므로 보다 복잡한 인터렉션을 위혀서는 UObject or AActor 서브클래스가 낫다.
구조체 작성 예시
//UStruct라는 Unreal 특유의 메크로를 선언 BlueprintType이라는 키워드 추가로 해당 구조체를 BP와 호환도는 데이터 성격을 가진다.
USTRUCT(BlueprintType)
//F로 시작하기에 일반 객체로 취급한다. -> Reflection System에 의해서 관리받지 못한다.
//활용이 보통 스택 내 데이터로 사용되기 때문에 관리받을 필요성이 없다.
struct FMyStruct
{
//구조체가 가진 각종 "속성"을 Reflection or 직렬화(Serialization)을 통해 활용할 수 있도록 한다.
//그리고 GENERATED_BODY()가 선언되면 UScriptStruct클래스로 구현된다. UFUNCTION()은 사용 불가
GENERATED_BODY()
//멤버 변수를 UPROPERTY를 통해 Reflection System에서 관리하도록 하고 다양하게 Unreal과 상호작용 할 수 있다.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Test Variables")
int32 MyIntegerMemberVariable;
//물론 UPROTPERTY를 작성하지 않고 CPP내에서만 사용해도 무관하다.
int32 NativeOnlyMemberVariable;
//다음과 같이 UPROPERTY만 작성해줘도 되고 특히 *는 Reflection System에서 메모리 관리를 해주기 위해
//반드시 UPROPERTY를 작성해야한다.
UPROPERTY()
UObject* SafeObjectPointer;
};
Unreal Struct 예시
#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "MyGameInstance.generated.h"
USTRUCT()
struct FStudentData
{
GENERATED_BODY()
FStudentData()
{
Name = TEXT("홍길동");
Order = -1;
}
//Unreal Object와 다르게 인자를 가진 생성자를 만들어도 무관하다. -> New API로 생성될 일이 없기 때문이다.
FStudentData(FString InName, int32 InOrder) : Name(InName), Order(InOrder) {}
//아래 TMap예시에서 TSet으로 Struct를 받는 방법 때문에 두 함수를 작성했다.
//Custom 구조체(FStudent)에 대해 operator==와 GetTypeHash 두가지를 정의해주지 않으면
//Hash값을 만들 수 없기 때문에 TSet&TMap에서
bool operator==(const FStudentData& InOther) const
{
//순번이 동일하면 같은 것으로 취급한다.
return Order == InOther.Order;
}
friend FORCEINLINE uint32 GetTypeHash(const FStudentData& InStudentData)
{
//FStudentData레퍼런스에 Order로 있는 값을 Hash값으로 넘겨준다.
return GetTypeHash(InStudentData.Order);
}
UPROPERTY()
FString Name;
UPROPERTY()
int32 Order;
};
UCLASS()
class UNREALCONTAINER_API UMyGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
virtual void Init() override;
private:
//h에서 FStudentData를 담을 TArray를 선언
//구조체이기에 UPROPERTY를 붙여도 되고 안붙여도 된다.
TArray<FStudentData> StudentsData;
//UObject는 UPROPERTY가 필수이다.
UPROPERTY()
//Unreal Object header에서 UnrealObject pointer를 선언할 때는 TObjectPtr을 사용
TArray<TObjectPtr<class UStudent>> Students;
//아래 TMap에서 활용
TMap<int32, FString> StudentsMap;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyGameInstance.h"
#include "Algo/Accumulate.h"
FString MakeRandomName() //랜덤으로 이름 만듦
{
TCHAR FirstChar[] = TEXT("김이박최");
TCHAR MiddleChar[] = TEXT("상혜지성");
TCHAR LastChar[] = TEXT("수은원연");
TArray<TCHAR> RandArray;
RandArray.SetNum(3); //3칸으로 만듦
RandArray[0] = FirstChar[FMath::RandRange(0, 3)];
RandArray[1] = MiddleChar[FMath::RandRange(0, 3)];
RandArray[2] = LastChar[FMath::RandRange(0, 3)];
return RandArray.GetData();
}
void UMyGameInstance::Init()
{
const int32 StudentNum = 300;
for (int32 ix = 1; ix <= StudentNum; ++ix)
{
//구조체는 복사비용이 발생하기 때문에 Add보단 Emplace가 낫다.
StudentsData.Emplace(FStudentData(MakeRandomName(), ix));
}
TArray<FString> AllStudentsNames;
//아래는 StudentData를 AllStudentsNames로 옮기는 과정이다.
//Transform의 1번째 인자는 Source, 2번째는 Output을 넣어준다.
//3번째 인자로 람다함수를 넣어주면 된다.
Algo::Transform(StudentsData, AllStudentsNames,
[](const FStudentData& Val)
{
return Val.Name;
}
);
UE_LOG(LogTemp, Log, TEXT("모든 학생 이름의 수 : %d"), AllStudentsNames.Num());
//Set은 중복을 허용하지 않음
TSet<FString> AllUniqueNames;
Algo::Transform(StudentsData, AllUniqueNames,
[](const FStudentData& Val)
{
return Val.Name;
}
);
UE_LOG(LogTemp, Log, TEXT("중복 없는 학생 이름의 수 : %d"), AllUniqueNames.Num());
Algo::Transform(StudentsData, StudentsMap,
[](const FStudentData& Val)
{
return TPair<int32, FString>(Val.Order, Val.Name);
}
);
}
TMap
- key, value 구성의 튜플(Tuple) 데이터의 TSet 구조로 구현이 되어 있어 내부구조는 TSet과 동일하다.
- 장, 단점 또한 TSet과 동일하다.
- TMap에선 key-value를 각 데이터가 내부적으로는 TPair<KeyType, ElementType>으로 되어있다.
- TMap도 TSet과 동일하게 Hash Container이기 때문에 key유형은 반드시 GetTypeHash를 지원하고 operater==가 제공되어야한다.
TMap의 내부구조
TSet과 동일하게 중간중간 빠져있는 것을 확인 가능하다.
TSet과의 차이점은 TPair<Key, Value>값으로 가지고 있다는 점이다.
TMap은 중복을 허용하지 않고 TMultiMap은 중복을 허용한다.
TMap 예시
//StudentsMap은 h파일에서 TMap<int32, FString> StudentsMap;으로 생성
Algo::Transform(StudentsData, StudentsMap, //동일하게 Transform으로 옮기는 작업이다.
[](const FStudentData& Val)
{
//내부적으로는 TPair가 들어가기 때문에 return값은 TPair로 설정해줘야한다.
return TPair<int32, FString>(Val.Order, Val.Name);
}
);
//TPair의 첫 인자가 int32(순번)이기 때문에 순번에 따른 학생 맵의 레코드 수이다.
UE_LOG(LogTemp, Log, TEXT("순번에 따른 학생 맵의 레코드 수 : %d"), StudentsMap.Num());
//중복이 되지 않아 TMap의 이름이 UniqueName이다.
TMap<FString, int32> StudentsMapByUniqueName;
Algo::Transform(StudentsData, StudentsMapByUniqueName,
[](const FStudentData& Val)
{
return TPair<FString, int32>(Val.Name, Val.Order);
}
);
//TPair의 첫 인자가 FString(이름)이기 때문에 이름에 따른 학생 맵의 레코드 수이다.
UE_LOG(LogTemp, Log, TEXT("이름에 따른 학생 맵의 레코드 수 : %d"), StudentsMapByUniqueName.Num());
//TMultiMap으로 중복 허용
TMultiMap<FString, int32> StudentMapByName;
Algo::Transform(StudentsData, StudentMapByName,
[](const FStudentData& Val)
{
return TPair<FString, int32>(Val.Name, Val.Order);
}
);
UE_LOG(LogTemp, Log, TEXT("이름에 따른 학생 멀티맵의 레코드 수 : %d"), StudentMapByName.Num());
const FString TargetName(TEXT("이혜은"));
TArray<int32> AllOrders;
//TargetName(이혜은)이라는 이름을 가진 모든 순번정보 레퍼런스를 AllOrders에 옮긴다.
//첫 인자는 Key, Value 값 중 key값을 넣어준다.
StudentMapByName.MultiFind(TargetName, AllOrders);
UE_LOG(LogTemp, Log, TEXT("이름이 %s인 학생 수 : %d"), *TargetName, AllOrders.Num());
//TSet에 Struct 구조를 넣는 방법이다.
TSet<FStudentData> StudentsSet;
for (int32 ix = 1; ix <= StudentNum; ++ix)
{
//FStudentData는 FString, int32를 받으므로 다음과 같이 작성했다.
//구조체이므로 Add보단 Emplace를 사용했다.
//TSet&TMap은 operater==와 GetTypeHash을 정의하지 않으면 다음과 같이 추가할 수 없다.
//위 Struct예시 h파일에 자세히 적어놨다.
StudentsSet.Emplace(FStudentData(MakeRandomName(), ix));
}
'Unreal 게임 개발 > Unreal 강의 개인 정리' 카테고리의 다른 글
Unreal 직렬화 (0) | 2024.01.08 |
---|---|
Unreal 메모리 관리 (GC) (0) | 2024.01.03 |
Unreal Delegate (0) | 2023.12.29 |
Unreal Composition & Enum UMETA 출력 (0) | 2023.12.28 |
Unreal Interface (0) | 2023.12.27 |