언리얼 컨테이너 라이브러리(UCL, Unreal Container Library)

언리얼 엔진이 자체 제작해 제공하는 자료구조 라이브러리

언리얼 오브젝트를 안정적으로 지원하면서 다수의 오브젝트 처리에 사용

실제 게임 제작에 사용되는 라이브러리는 TArray, TSet, TMap (T는 Template Library를 의미)

가볍고 게임 제작에 최적화됨

 

C++ STL

범용적으로 설계됨

표준이기 때문에 호환성이 높음

많은 기능이 있어 컴파일 시간이 오래 걸림

 

TArray

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/array-containers-in-unreal-engine?application_version=5.1

 

STL vector와 유사

시퀀스 컨테이너, 가변 배열 자료구조

데이터가 순차적으로 모여있기 때문에 메모리를 효과적으로 사용할 수 있고 캐시 효율도 높음

컴퓨터 사양이 좋아지면서, 캐시 지역성으로 인한 성능 향상이 중요해짐

 

임의 데이터 접근이 빠르고, 빠르게 순회 가능

맨 끝에 데이터를 추가하는 것은 가볍지만 중간에 요소를 추가하거나 삭제하는 비용이 큼

데이터가 많아질수록 검색, 삭제, 수정 작업이 느리기 때문에 많은 수의 데이터일 경우 TSet을 고려

 

멤버 함수

GetData : 배열을 시작하는 부분의 포인터를 가져옴

FString* StrPtr = StrArr.GetData();

 

Add : 추가할 데이터를 생성한 후 TArray에 이동/복사해서 넣는 방식, 가독성 높음

Emplace : 생성자의 매개변수를 전달하면 TArray 자체에서 바로 생성

Add보다 효율 높음, 가독성 낮음
구조체 이상의 크기를 가진 데이터를 넣을 때 적극 사용

 

Append, += : TArray / 배열을 추가할 때 사용

AddUnique : 요소에 존재하지 않는 경우에만 추가, 보통 TSet이 더 유용

TArray<FString> StrArr;

StrArr.Add(TEXT("Hello"));

StrArr.Emplace(TEXT("Hello")); // StrArr == ["Hello", "Hello"]

FString Arr[] = { TEXT("of"), TEXT("Tomorrow") };
StrArr.Append(Arr, ARRAY_COUNT(Arr));
StrArr += Arr;

 

Num() : 배열 Size 반환

SetNum() : 배열의 크기를 설정, 현재 크기보다 작은 경우 요소 제거

 

GetSlack : 배열의 메모리 여유분을 반환, vector의 capacity와 유사

Shrink : 요소가 없는 Slack을 제거하여 불필요한 메모리 감소

int32 Count = StrArr.Num();

StrArr.SetNum(8);

SlackArray.GetSlack();

SlackArray.Shrink();

 

Insert : 중간에 추가

Remove : 해당하는 요소를 삭제

StrArr.Insert(TEXT("Brave"), 1);

StrArr.Remove(TEXT("Brave"));

 

operator[] : 인덱스 접근, 레퍼런스로 반환, 균일한 데이터로 배열되기 때문에 주소를 한번에 알 수 있어서 빠름
대입 연산자를 사용하면 Add 함수처럼 복사

StrArr[0] = TEXT("Hello");

 

Init : 기본 값을 채우는 함수

AddUninitialized / InsertUninitialized : 초기화되지 않는 메모리 추가, 빠르게 메모리 할당 가능

IntArray.Init(10, 5); // IntArray == [10,10,10,10,10]

int32 SrcInts[] = { 2, 3, 5, 7 };
TArray<int32> UninitInts;
UninitInts.AddUninitialized(4);
FMemory::Memcpy(UninitInts.GetData(), SrcInts, 4*sizeof(int32)); // UninitInts == [2,3,5,7]

 

Contains : 특정 요소가 들어있는지 확인하는 함수

IsValidIndex : 인덱스가 유효한지 확인하는 함수

bool bIsExistHello = StrArr.Contains(TEXT("Hello"));

bool bValidM1 = StrArr.IsValidIndex(-1); // bValidM1 == false

 

CreateIterator/CreateConstIterator : Iterator/ConstIterator 반환하는 함수

for (auto It = StrArr.CreateConstIterator(); It; ++It)
{
	JoinedStr += *It;
	JoinedStr += TEXT(" ");
}

 

Empty : 모든 요소 제거

StrArr.Empty(); // StrArr == []

 

MoveTemp : 데이터 이동

StrArr1 = MoveTemp(StrArr);

 

Heapify : 이진 힙 데이터 구조로 변환

TArray<int32> HeapArr;

for (int32 Val = 10; Val != 0; --Val)
{
	HeapArr.Add(Val); // HeapArr == [10,9,8,7,6,5,4,3,2,1]
}

HeapArr.Heapify();	// HeapArr == [1,2,4,3,6,5,8,10,7,9]

 

Top : 배열 끝 요소 반환

== / != : 배열이 같은 경우는 요소 수와 값들이 같을 경우 true

Sort(퀵 정렬로 진행하다가 깊이가 너무 크면 힙 정렬로 전환됨), HeapSort, StableSort(합병 정렬)

 

TSet

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/set-containers-in-unreal-engine?application_version=5.1

 

STL set

이진트리로 구성되어 정렬이 자동 적용되므로 불필요한 정렬이 일어날 수 있음

요소가 삭제될 때 균형을 위한 트리 구조 재구축이 일어날 수 있음

모든 요소를 순회하는데 적합하지 않음

 

TSet

STL unorederd_set과 유사

해시테이블 형태로 키 데이터가 구축되어 추가, 검색, 제거가 빠름

요소가 있는 TArray를 따로 유지하여 빠르게 순회 가능

비어 있는 데이터가 존재 가능하여 요소가 삭제되거나 테이블이 리사이즈될 때 중간에 비어있는 버킷이 있을 수 있음

 

사용자 정의 타입으로 구성할 경우 GetTypeHash 함수를 정의해야 함

 

멤버 함수

TArray와 유사

 

Find는 찾으면 해당 요소 포인터를 반환, 없으면 nullptr 반환

 

Index : Set의 Index를 의미하는 FSetElementId 반환

추가할 때 마지막 Invalid 부터 채워지므로 순서가 보장되지 않기 때문에 삭제할 때 인덱스로 제거하는 것은 권장 X, 키 값으로 제거

FSetElementId BananaIndex = FruitSet.Index(TEXT("Banana"));

 

Array : 비어있는 메모리 없이 Array로 변경

TArray<FString> FruitArray = FruitSet.Array();

 

Reset : 모든 요소를 invalid slack으로 변경

Shrink : 뒤 쪽에 있는 invalid slack을 제거

FruitSet.Reset();
// FruitSet == [ <invalid>, <invalid>, invalid> ] // invalid : 비어있는 slack

// FruitSet == [ <invalid>, "Banana", <invalid> ]
FruitSet.Shrink();
// FruitSet == [ <invalid>, "Banana" ]

 

TMap

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/map-containers-in-unreal-engine?application_version=5.1

 

STL Map

STL Set과 동일하게 이진트리로 구성

정렬 자동 적용, 데이터 삭제시 재구축 가능

순회하는데 적합하지 않음

 

Unreal TMap

TSet과 동일한 구조, 동일한 장단점, 유사한 멤버 함수

TPair<Key, Value>를 가진 튜플 데이터로 구성됨

TMultiMap을 사용하여 중복 데이터를 관리할 수 있음

구조체에서 동일 기준이 2개 이상일 때 MapKeyFuncs 사용

 

멤버 함수

값으로 키 찾는 함수, 최악의 경우 모든 요소 순회

const int32* KeyMangoPtr = FruitMap.FindKey(TEXT("Mango"));

 

Key/Value를 TArray로 얻는 함수

TArray<int32> FruitKeys;
TArray<FString> FruitValues;
FruitMap.GenerateKeyArray(FruitKeys);
FruitMap.GenerateValueArray(FruitValues);

 

TMultimap의 중복된 키를 찾아 TArray에 넣는 함수

const FString TargetName(TEXT("홍길동"));
TArray<int32> AllOrders;
StudentsMapByName.MultiFind(TargetName, AllOrders);

 

새로운 타입을 정의하여 키로 사용할 경우에 아래 두 함수를 추가해야 함

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

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

 

  TArray TSet TMap
접근 O(1) O(1) O(1)
검색 O(N) O(1) O(1)
삽입 O(N) O(1) O(1)
삭제 O(N) O(1) O(1)

TArray는 비어있는 메모리가 없으므로 탐색 속도가 더 빠름

 

구조체 UStruct

단순한 데이터 타입에 적합

언리얼 엔진이 일반 객체로 취급하여 리플리케이션을 사용하지 못하지만 UPROPERTY로 선언한 멤버 변수는 가능

 

GENERATED_BODY를 선언

UScriptStruct 클래스로 구현됨

리플렉션, 직렬화, 데이터 전송 등 가능

C++과 다르게 함수는 선언할 수 없음

USTRUCT()
struct FMyStruct
{
    GENERATED_BODY()
    
    UPROPERTY()
    TObjectPtr<UObject> SafeObjectPointer; // UPROPERTY로 선언하여 자동으로 메모리 관리
};

'언리얼 > 언리얼 C++ 및 개념' 카테고리의 다른 글

언리얼 오브젝트 직렬화  (0) 2024.07.20
언리얼 가비지 컬렉션  (0) 2024.07.18
언리얼 C++ 델리게이트  (0) 2024.07.11
언리얼 C++ 설계  (0) 2024.07.09
언리얼 오브젝트  (0) 2024.07.04

+ Recent posts

목차