강의 내용
언리얼에서 제공하는 대표 컨텐이너 라이브러리의 동작 원리와 활용 방법을 예제를 통해 살펴보기
강의 목표
언리얼 대표 컨테이너 라이브러리 TArray, TSet의 내부 구조 이해
각 컨테이너 라이브러리의 장단점을 파악하고, 알맞게 활용하는 방법의 학습
언리얼 컨테이너 라이브러리
언리얼이 자체 제공하는 라이브러리
UCL이라고도 함
TArray, TMap, TSet 추천
T는 Template Library를 의미
C++STL과 언리얼 컨테이너 라이브러리의 차이점
언리얼에 특화, 가볍과 게임 제작에 최적화
UCL 주요 라이브러리
vector 거의 비슷 TArray
set 좀 다름 TSet
map 좀 다름 TMap
TArray의 구조와 활용
TArray 개요
vector와 유사
데이터 많아지면 검색, 삭제, 수정이 느려지니 데이터 많고, 검색 많이 할거면 TSet 쓰는게 좋아.
TArray 내부 구조
앞주소 GetData() <- 데이터 배열 <- 뒤에 추가 Add, Emplace, Append
Insert로 중간에 넣고, []로 중간 값 구할 수 있음
Add는 단일 항목을 복사하여 추가하는 데 사용되며, Emplace는 항목을 직접 생성하여 추가하는 데 사용되고, Append는 다른 TArray의 항목들을 현재 배열에 추가하는 데 사용된다.
TArray 언리얼 공식 문서
https://docs.unrealengine.com/5.3/ko/array-containers-in-unreal-engine/
TArray: 언리얼 엔진의 배열
docs.unrealengine.com
TArray 실습
예제를 통해 다뤄보자.
UnrealContainer라는 이름으로 언리얼 프로젝트를 생성한다.
GameInstance를 상속받은 MyGameInstance를 생성한다.
그리고 프로젝트 세팅에서 Map을 clear하고, MyGameInstance를 GameInstance로 지정한다.
언리얼 에디터를 종료하고 비쥬얼 스튜디오에서 작업한다.
MyGameInstance에서 Init함수를 오버라이드 해서 구현해보자.
UCLASS()
class UNREALCONTAINER_API UMyGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
virtual void Init() override;
};
구현부는
#include "MyGameInstance.h"
void UMyGameInstance::Init()
{
Super::Init();
const int32 ArrayNum = 10;
TArray<int32> Int32Array; // 이 때는 메모리 차지하지 않는 빈 상태
for (int32 ix = 1; ix <= ArrayNum; ++ix)
{
Int32Array.Add(ix);
}
Int32Array.RemoveAll(
[](int32 Val)
{
return Val % 2 == 0;
}
);
Int32Array += {2, 4, 6, 8, 10};
TArray<int32> Int32ArrayCompare;
int32 CArray[] = { 1,3,5,7,9,2,4,6,8,10 };
Int32ArrayCompare.AddUninitialized(ArrayNum);
FMemory::Memcpy(Int32ArrayCompare.GetData(), CArray, sizeof(int32) * ArrayNum);
ensure(Int32Array == Int32ArrayCompare);
}
빌드를 하고,
디버그 없이 실행을 해서 언리얼 에디터에서 문제가 없는지 살펴 보자. 만약 문제가 있다면 실행을 할 때 ensure함수에 의해서 빨간색 에러가 뜰 것이다.
필터를 설정해 보면 Error가 없다는 것을 알 수 있다.
이번엔 언리얼 에디터를 끄고 TArray 안이 어떻게 생겼는지 브레이크 포인터를 잡아 살펴보자. Int32Array +={2,4,6,8,10}; 에 브레이크 포인터를 잡고 F5키를 누르면 언리얼 에디터가 실행이 되고 플레이 버튼을 누르면 브레이크 포인트가 걸린다.

변수를 Watch창에 넣어도 볼 수 없다고 뜬다. 이유는 지금은 실행 흐름을 디버깅 할 수 있는 모드지만, 내부 내용까지 추적할 수 있는 상세 모드로 빌드가 되지 않았다.
중지를 누르고 비주얼 스튜디오를 보면 Development Editor가 있다. 이건 상세 내용을 볼 수 없다. DebugGame Editor로 바꿔서 빌드를 하고 디버깅을 해줘야 한다.
F5키를 눌러서 언리얼이 실행되면 플레이를 누르면 이제 내용을 볼 수 있다.

짝수를 뺐기 때문에 1,3,5,7,9가 들어가 있는 것을 볼 수 있다.
Int32Array += {2, 4, 6, 8, 10}; 이 코드 이후에는

이렇게 2,4,6,8,10이 추가 된 것을 볼 수 있다.
Int32ArrayCompare도 보면

이렇게 같게 초기화 된 것을 볼 수 있다. 그래서 ensure 구문을 통과하는 것을 확인할 수 있다.
이걸 효과적으로 활용할 수 있는 알고리즘 라이브러리를 사용해 보자.
합을 구하는 알고리즘을 사용해보자. #include "Algo/Accumulate.h”를 추가해주면 Accumulate 함수를 사용할 수 있다.
int32 Sum = 0;
for (const int32& Int32Elem : Int32Array)
{
Sum += Int32Elem;
}
ensure(Sum == 55);
}
이렇게 ensure을 통과할 수 있을거야. 하지만 이렇게 만드는 게 복잡하다 느껴질 수 있어. 그 때 Accumulate 함수를 사용하면 총합을 간단하게 계산할 수 있다.
int32 SumByAlgo = Algo::Accumulate(Int32Array, 0);
ensure(Sum == SumByAlgo);
}
다시 f5를 눌러서 언리얼을 실행하고 프레이를 눌러 ensure을 통과하는지 확인을 해보자.
Error가 뜨지 않은 것을 볼 수 있다.
TSet의 구조와 활용
TSet의 특징
STL set과 다르니 주의
- 이진트리 vs 해시테이블
- 요소 삭제시 균형 위해 재구축 vs 재구축 안함
- 순회하는데 부적합 vs 빠른 순회 가능
unordered_set과 유사하지만 같진 않아.
TSet은 중복 없는 데이터 집합을 구축할 때 유의
TSet의 내부 구조
Iterator <- 이빠진 데이터 배열
Add로 더하고, Find로 값 구함
TSet 공식문서
https://docs.unrealengine.com/5.3/ko/set-containers-in-unreal-engine/
TSet
TSet, 세트는 보통 순서가 중요치 않은 상황에서 고유 엘리먼트를 저장하는 데 사용되는 고속 컨테이너 클래스입니다.
docs.unrealengine.com
TSet 실습
TSet<int32> Int32Set;
for (int32 ix = 1; ix <= ArrayNum; ++ix)
{
Int32Set.Add(ix);
}
Int32Set.Remove(2);
Int32Set.Remove(4);
Int32Set.Remove(6);
Int32Set.Remove(8);
Int32Set.Remove(10);
Int32Set.Add(2);
Int32Set.Add(4);
Int32Set.Add(6);
Int32Set.Add(8);
Int32Set.Add(10);
}
이렇게 했을 때 Tset의 경우 TArray와 달리 내부 자료구조가 어떻게 달라졌을지 확인을 해보자.
F5키를 눌러 디버그 모드로 실행해보자.

아직 Remove를 하기 전이라 1부터 10까지의 수가 들어가 있다.

겉으로는 수가 9개로 줄어들었지만 안을 보면 그대로 10개고 Remove된 게 Invalid로 되어 있는 것을 볼 수 있다. 제거할 때 마다 이빨이 빠지는 형태를 갖게 된다.

5개지만 메모리는 10개 차지하고 있는 걸 볼 수 있다.
Add를 하면 마지막에 빠진 요소에 빈틈을 채워넣는 방식으로 작동한다.

Add(2)를 하면 마지막으로 뺀 index9가 2로 채워지는 것을 볼 수 있다.
차례대로 거꾸로 채워지는 패턴이 나오게 된다.

그래서 이렇게 2,4,6,8,10이 거꾸로 채워진 형태가 된다.
이런 것들을 예측해서 데이터의 순서를 조절하는 건 사실상 어렵기 떄문의 TSet를 사용할 땐 순번은 생각하지 않고 무작위로 섞여있는 집합처럼 다뤄주는 것이 좋다.
자료구조의 시간 복잡도 비교
TArray TSet
접근 O(1) O(1)
검색 O(N) O(1)
삽입 O(N) O(1)
삭제 O(N) O(1)
TArray: 빈틈없는 메모리, 가정 높은 접근 성능, 가장 높은 순회 성능
TSet: 빠른 중복 감지
정리
1. TArray, TSet 컨테이너 라이브러리 내부 구조와 활용 방법
2. 디버그 빌드를 사용해 메모리 정보를 확인하는 방법의 학습
3. 두 컨테이너 라이브러리가 가진 특징의 이해
'Unreal engine > Unreal C++' 카테고리의 다른 글
| 3_3_언리얼 메모리 관리 시스템(가비지 컬렉션) (0) | 2023.10.12 |
|---|---|
| 3_2_UStruct(언리얼 구조체)와 Map (0) | 2023.10.05 |
| 2_3_언리얼 C++ 델리게이트로 구현하는 느슨한 결합 (0) | 2023.09.28 |
| 2_2_언리얼 C++ 모던객체지향 설계_컴포지션 (0) | 2023.09.25 |
| 2_1_언리얼 C++ 모던객체지향 설계_인터페이스 (0) | 2023.09.22 |
댓글