โš™๏ธ ์—”์ง„/๐Ÿซ ์–ธ๋ฆฌ์–ผ

[Unreal] ์–ธ๋ฆฌ์–ผ ์ปจํ…Œ์ด๋„ˆ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ2 - ๊ตฌ์กฐ์ฒด์™€ TMap

peewoong 2024. 5. 4. 23:42

๐Ÿง 1. ์–ธ๋ฆฌ์–ผ ๊ตฌ์กฐ์ฒด

  • ๋ฐ์ดํ„ฐ ์ €์žฅ/์ „์†ก์— ํŠนํ™”๋œ ๊ฐ€๋ฒผ์šด ๊ฐ์ฒด
  • ๋Œ€๋ถ€๋ถ„ GENERATED_BODY ๋งคํฌ๋กœ๋ฅผ ์„ ์–ธํ•ด์ค€๋‹ค.
    • ๋ฆฌํ”Œ๋ ‰์…˜, ์ง๋ ฌํ™”์™€ ๊ฐ™์€ ์œ ์šฉํ•œ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•จ
    • GENERATED_BODY๋ฅผ ์„ ์–ธํ•œ ๊ตฌ์กฐ์ฒด๋Š” UScriptStruct ํด๋ž˜์Šค๋กœ ๊ตฌํ˜„๋จ
    • ์ด ๊ฒฝ์šฐ ์ œํ•œ์ ์œผ๋กœ ๋ฆฌํ”Œ๋ ‰์…˜์„ ์ง€์›ํ•จ ๐Ÿ‘‰ ์†์„ฑ UPROPERTY๋งŒ ์„ ์–ธํ•  ์ˆ˜ ์žˆ๊ณ  ํ•จ์ˆ˜ UFUNCTION์€ ์„ ์–ธํ•  ์ˆ˜ ์—†์Œ
  • ์–ธ๋ฆฌ์–ผ ์—”์ง„์˜ ๊ตฌ์กฐ์ฒด ์ด๋ฆ„์€ F๋กœ ์‹œ์ž‘ํ•จ
    • ๋Œ€๋ถ€๋ถ„ ํž™ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น(ํฌ์ธํ„ฐ ์—ฐ์‚ฐ)์—†์ด ์Šคํƒ ๋‚ด ๋ฐ์ดํ„ฐ๋กœ ์‚ฌ์šฉ๋จ
    • NewObjectAPI๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Œ

 

๐ŸŒŸ ๋ฆฌํ”Œ๋ ‰์…˜ ๊ด€๋ จ ๊ณ„์ธต ๊ตฌ์กฐ

์–ธ๋ฆฌ์–ผ์—์„œ๋Š” ๊ตฌ์กฐ์ฒด์™€ ํด๋ž˜์Šค๊ฐ€ ๊ตฌ์กฐ, ์‚ฌ์šฉ๋ฒ•์ด ๋‹ค๋ฅด๋‹ค.

 

๐ŸŒŸ MyGameInstance(h, cpp)

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "MyGameInstance.generated.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
{
	GENERATED_BODY()
	
public:
	virtual void Init() override;
	
private:
	TArray<FStudentData> StudentsData;

	UPROPERTY()
	TArray<TObjectPtr<class UStudent>> Students;
};
// ๋žœ๋ค์œผ๋กœ ์ด๋ฆ„ ๋งŒ๋“ค๊ธฐ(Init ์™ธ๋ถ€)
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();
}


// ๊ตฌ์กฐ์ฒด (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์œผ๋กœ ํ‘œํ˜„
TSet<FString> AllUniqueNames;
Algo::Transform(StudentsData, AllUniqueNames,
	[](const FStudentData& Val) {
		return Val.Name;
	}
);

UE_LOG(LogTemp, Log, TEXT("์ค‘๋ณต ์—†๋Š” ํ•™์ƒ ์ด๋ฆ„์˜ ์ˆ˜ : %d"), AllUniqueNames.Num());

 

๐ŸŸฉ ๊ฐ์ฒด์˜ ๋™์  ๋ฐฐ์—ด ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ์˜ˆ์ œ ๋‹ค์ด์–ด๊ทธ๋žจ

์–ธ๋ฆฌ์–ผ ์˜ค๋ธŒ์ ํŠธ ํ•™์ƒ์˜ ๋™์  ๋ฐฐ์—ด ๊ด€๋ฆฌ ๋ฐฉ๋ฒ•

์–ธ๋ฆฌ์–ผ ๊ตฌ์กฐ์ฒด ํ•™์ƒ ์ •๋ณด์˜ ๋™์  ๋ฐฐ์—ด ๊ด€๋ฆฌ ๋ฐฉ๋ฒ•

// ๊ตฌ์กฐ์ฒด
TArray<FStudentData> StudentsData;

// ์˜ค๋ธŒ์ ํŠธ
UPROPERTY()
TArray<TObjectPtr<class UStudent>> Students;

๐Ÿง 2. TMap

๐ŸŒŸ STL map ๊ณผ TMap์˜ ๋น„๊ต

  • STL map์˜ ํŠน์ง•
    • STL set๊ณผ ๋™์ผํ•˜๊ฒŒ ์ด์ง„ํŠธ๋ฆฌ๋กœ ๊ตฌ์„ฑ
    • ์ •๋ ฌ์€ ์ง€์›ํ•˜์ง€๋งŒ, ๋ฉ”๋ชจ๋ฆฌ ๊ตฌ์„ฑ์ด ํšจ์œจ์ ์ด์ง€ ์•Š์œผ๋ฉฐ, ๋ฐ์ดํ„ฐ ์‚ญ์ œ์‹œ ์žฌ๊ตฌ์ถ•์ด ์ผ์–ด๋‚  ์ˆ˜ ์žˆ์Œ
    • ๋ชจ๋“  ์ž๋ฃŒ๋ฅผ ์ˆœํšŒํ•˜๋Š”๋ฐ ์ ํ•ฉํ•˜์ง„ ์•Š์Œ
  • ์–ธ๋ฆฌ์–ผ TMap์˜ ํŠน์ง•
    • ํ‚ค, ๋ฐธ๋ฅ˜ ๊ตฌ์„ฑ์˜ ํŠœํ”Œ ๋ฐ์ดํ„ฐ์˜ TSet ๊ตฌ์กฐ๋กœ ๊ตฌํ˜„๋˜์–ด ์žˆ์Œ
    • ํ•ด์‹œํ…Œ์ด๋ธ” ํ˜•ํƒœ๋กœ ๊ตฌ์ถ•๋˜์–ด ์žˆ์–ด ๋น ๋ฅธ ๊ฒ€์ƒ‰์ด ๊ฐ€๋Šฅํ•จ
    • ๋™์  ๋ฐฐ์—ด์˜ ํ˜•ํƒœ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ชจ์—ฌ์žˆ์Œ
    • ๋ฐ์ดํ„ฐ๋Š” ๋น ๋ฅด๊ฒŒ ์ˆœํšŒํ•  ์ˆ˜ ์žˆ์Œ
    • ๋ฐ์ดํ„ฐ๋Š” ์‚ญ์ œํ•ด๋„ ์žฌ๊ตฌ์ถ•์ด ์ผ์–ด๋‚˜์ง€ ์•Š์Œ
    • ๋น„์–ด์žˆ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Œ
    • TMultiMap์„ ์‚ฌ์šฉํ•˜๋ฉด ์ค‘๋ณต ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Œ
  • ๋™์ž‘ ์›๋ฆฌ๋Š” STL unordered_map๊ณผ ์œ ์‚ฌํ•จ
  • ํ‚ค, ๋ฐธ๋ฅ˜ ์Œ์ด ํ•„์š”ํ•œ ์ž๋ฃŒ๊ตฌ์กฐ์— ๊ด‘๋ฒ”์œ„ํ•˜๊ฒŒ ์‚ฌ์šฉ๋จ
  • TSet๊ณผ ์œ ์‚ฌํ•˜์ง€๋งŒ TPair<key, value>์˜ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง„๋‹ค

๐ŸŒŸ MyGameInstance(h, cpp)

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

	friend FORCEINLINE uint32 GetTypeHash(const FStudentData& InStudentData) {
		return GetTypeHash(InStudentData.Order);
	}
// TMap (๊ธฐ๋ณธ์ ์œผ๋กœ ์ค‘๋ณต ํ—ˆ์šฉO)
Algo::Transform(StudentsData, StudentsMap,
	[](const FStudentData& Val) {
		return TPair<int32, FString>(Val.Order, Val.Name);
	}
);

UE_LOG(LogTemp, Log, TEXT("์ˆœ๋ฒˆ์— ๋”ฐ๋ฅธ ํ•™์ƒ ๋งต์˜ ๋ ˆ์ฝ”๋“œ ์ˆ˜ : %d"), StudentsMap.Num());

// ์ค‘๋ณต ํ—ˆ์šฉX
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());

// ์ค‘๋ณต ํ—ˆ์šฉO
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์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ
TSet<FStudentData> StudentsSet;
for (int32 ix = 1; ix <= StudentNum; ++ix) {
	StudentsSet.Emplace(FStudentData(MakeRandomName(), ix));
}

 

๐ŸŒŸ ์‹œ๊ฐ„๋ณต์žก๋„ ๋น„๊ต

  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)

 

TArray ๐Ÿ‘‰ ๋นˆํ‹ˆ์—†๋Š” ๋ฉ”๋ชจ๋ฆฌ, ๊ฐ€์žฅ ๋†’์€ ์ ‘๊ทผ์„ฑ๋Šฅ, ๊ฐ€์žฅ ๋†’์€ ์ˆœํšŒ์„ฑ๋Šฅ

TSet ๐Ÿ‘‰ ๋น ๋ฅธ ์ค‘๋ณต ๊ฐ์ง€

TMap ๐Ÿ‘‰ ์ค‘๋ณต ๋ถˆํ—ˆ, ํ‚ค ๋ฐธ๋ฅ˜ ๊ด€๋ฆฌ

TMultiMap ๐Ÿ‘‰ ์ค‘๋ณต ํ—ˆ์šฉ, ํ‚ค ๋ฐธ๋ฅ˜ ๊ด€๋ฆฌ