본문 바로가기
Unreal 게임 개발/Unreal 강의 개인 정리

Unreal Struct & Map

by daisy0461 2024. 1. 3.

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