본문 바로가기
Unreal/[서적] 언리얼5 이득우님 인프런1

Ch 11. 구조체와 Map

by GameStudy 2023. 5. 13.

11.1 언리얼 구조체 특징과 선언

11.1-1 USTRUCT

  - 데이터 저장/전송에 특화된 가벼운 개체

  - 대부분 GENERATED_BODY 매크로를 선언해줌.

    리플렉션/직렬화와 같은 유용한 기능을 지원함.

    GENERATED_BODY를 선언한 구조체는 UScriptStruct 클래스로 구현됨.

    이경우 제한적으로 리플렉션을 지원함. 

    속성 UPROPERTY만 선언할 수 있고, 함수 UFUNCTION은 선언할 수 없음.

  - 언리얼 엔진의 구조체 이름은 F로 시작함. 

  - 대부분 스택 메모리에 저장됨. 힙 메모리 할당(포인터 연산) 없음. 저장/전송에 특화되기 때문.

    NewObject API를 사용할 수 없음.

 

11.1-2 언리얼 리플렉션 관련 계층 구조

위 그림에서 알 수 있듯, USTRUCT와 UCLASS는 사용처가 다름.

 

11.1-3 언리얼 구조체 실습

<hide/>

// Student.h

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "Student.generated.h"

UCLASS()
class UNREALCONTAINER_API UStudent : public UObject
{
	GENERATED_BODY()
	
};
<hide/>

// Student.cpp

#include "Student.h"
<hide/>

// MyGameInstance.h

...

USTRUCT()
struct FStudentData
{
	GENERATED_BODY()
	
	FStudentData()
	{
		Name = TEXT("ȫ�浿");
		Order = -1;
	}

	FStudentData(FString InName, int32 InOrder) : Name(InName), Order(InOrder) {}

	UPROPERTY() // 리플렉션 시스템이 필요 없는 프로퍼티라면 안붙혀도 됨. 안붙히면 컴파일 시간을 줄일 수 있음.
	FString Name;

	UPROPERTY()
	int32 Order;
};

UCLASS()
class UNREALCONTAINER_API UMyGameInstance : public UGameInstance
{
	...
	
private:
	TArray<FStudentData> StudentsData;
    
    UPROPERTY()
	TArray<TObjectPtr<class UStudent>> Students;

	...
};
<hide/>

// MyGameInstance.cpp

...

FString MakeRandomName()
{
	TCHAR FirstChar[] = TEXT("김이박최");
	TCHAR MiddleChar[] = TEXT("상혜지성");
	TCHAR LastChar[] = TEXT("수은원연");

	TArray<TCHAR> RandArray;
	RandArray.SetNum(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)
	{
		StudentsData.Emplace(FStudentData(MakeRandomName(), ix));
	}

	TArray<FString> AllStudentsNames;
	Algo::Transform(StudentsData, AllStudentsNames,
		[](const FStudentData& Val)
		{
			return Val.Name;
		}
	);

	UE_LOG(LogTemp, Log, TEXT("모든 학생 이름의 수 : %d"), AllStudentsNames.Num());

	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);
		}
	);

	
}

 

11.2 언리얼 TMap

11.2-1 TMap의 특징

  - STL의 unordered_map과 유사함. 키-벨류 쌍 자료구조가 필요한 경우에 광범위하게 사용됨.

  - 비어있는 요소가 있을 수 있고, TMultiMap을 사용하면 중복 데이터도 관리할 수 있음.

STL Map UCL TMap
이진 트리 기반. 해시 테이블 기반. 빠른 검색 가능.
메모리 구성이 비효율적. 동적 배열 형태라 메모리 구성이 효율적.
데이터 삭제시 재구축이 일어날 수 있음. 재구축이 일어나지 않음.
모든 자료를 순회하는데 적합하지 않음. 빠르게 순회할 수 있음.

TMap 내부 구조

11.2-3 해시 테이블 기반의 TSet과 TMap의 주의 사항

  - 커스텀 자료형을 만들어서 키 값으로 사용하려면

    특정 연산자를 오버로딩 해줘야함. 이에 대한 내용은 [여기]에서 확인할 수 있음.

 

11.2-2 TMap 실습

<hide/>

// MyGameInstance.h

...

USTRUCT()
struct FStudentData
{
	...

	FStudentData(FString InName, int32 InOrder) : Name(InName), Order(InOrder) {}

	bool operator==(const FStudentData& InOther) const
	{
		return Order == InOther.Order;
	}

	friend FORCEINLINE uint32 GetTypeHash(const FStudentData& InStudentData)
	{
		return GetTypeHash(InStudentData.Order);
	}

	UPROPERTY() 
	FString Name;

	...
};

UCLASS()
class UNREALCONTAINER_API UMyGameInstance : public UGameInstance
{
	...

	TMap<int32, FString> StudentsMap;
};
<hide/>

// MyGameInstance.cpp

...

void UMyGameInstance::Init()
{
	...


	Algo::Transform(StudentsData, StudentsMap,
		[](const FStudentData& Val)
		{
			return TPair<int32, FString>(Val.Order, Val.Name);
		}
	);

	UE_LOG(LogTemp, Log, TEXT("순번에 따른 학생 맵의 레코드 수 : %d"), StudentsMap.Num());

	TMap<FString, int32> StudentsMapByUniqueName;

	Algo::Transform(StudentsData, StudentsMapByUniqueName,
		[](const FStudentData& Val)
		{
			return TPair<FString, int32>(Val.Name, Val.Order);
		}
	);

	UE_LOG(LogTemp, Log, TEXT("이름에 따른 학생 맵의 레코드 수 : %d"), StudentsMapByUniqueName.Num());

	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;
	StudentMapByName.MultiFind(TargetName, AllOrders);

	UE_LOG(LogTemp, Log, TEXT("이름이 %s인 학생 수 : %d"), *TargetName, AllOrders.Num());

	TSet<FStudentData> StudentsSet;
	for (int32 ix = 1; ix <= StudentNum; ++ix)
	{
		StudentsSet.Emplace(FStudentData(MakeRandomName(), ix));
	}
}

 

11.2-3 UCL 핵심 자료구조의 시간 복잡도

  TArray
(캐시지역성, 임의접근)
TSet
(빠른 중복 감지)
TMap
(키-벨류 관리)
TMultiMap
(중복 허용 키-벨류)
접근 O(1) O(1) O(1) O(1)
검색 O(N) O(1) O(1) O(1)
삽입 O(N) O(1) O(1) O(1)
삭제 O(N) O(1) O(1) O(1)

 

'Unreal > [서적] 언리얼5 이득우님 인프런1' 카테고리의 다른 글

Ch 13. 직렬화  (0) 2023.05.13
Ch 12. 언리얼 엔진의 메모리 관리  (0) 2023.05.13
Ch 10. Array and Set  (0) 2023.05.12
Ch 09. 델리게이트  (0) 2023.05.12
Ch 08. 컴포지션  (0) 2023.05.11

댓글