KoreanFoodie's Study
언리얼 컨테이너 (Unreal Containers : TArray, TMap, TSet) 본문
Game Dev/Unreal C++ : Study
언리얼 컨테이너 (Unreal Containers : TArray, TMap, TSet)
GoldGiver 2022. 4. 13. 12:26TArray
TArray 는 std::vector 와 비슷하게 동작하지만, AddUnique, RemoveAtSwap 등의 부가적인 API 를 제공한다. TArray 는 빠르고, 메모리를 효율적으로 사용하며, 안전하다!
TArray<AActor*> ActorArray = GetActorArrayFromSomewhere();
// Tells how many elements (AActors) are currently stored in ActorArray.
int32 ArraySize = ActorArray.Num();
// TArrays are 0-based (the first element will be at index 0)
int32 Index = 0;
// Attempts to retrieve an element at the given index
AActor* FirstActor = ActorArray[Index];
// Adds a new element to the end of the array
AActor* NewActor = GetNewActor();
ActorArray.Add(NewActor);
// Adds an element to the end of the array only if it is not already in the array
ActorArray.AddUnique(NewActor); // Won't change the array because NewActor was already added
// Removes all instances of 'NewActor' from the array
ActorArray.Remove(NewActor);
// Removes the element at the specified index
// Elements above the index will be shifted down by one to fill the empty space
ActorArray.RemoveAt(Index);
// More efficient version of 'RemoveAt', but does not maintain order of the elements
ActorArray.RemoveAtSwap(Index);
// Removes all elements in the array
ActorArray.Empty();
TArray 는 컨테이너의 element 들이 가비지 컬렉팅된다는 이점이 있다. 이는 TArray 가 UObject 에서 파생된 포인터들을 담을 수 있다는 뜻이다.
UCLASS()
class UMyClass : UObject
{
GENERATED_BODY();
// ...
UPROPERTY()
AActor* GarbageCollectedActor;
UPROPERTY()
TArray<AActor*> GarbageCollectedArray;
TArray<AActor*> AnotherGarbageCollectedArray;
};
TMap
TMap 은 std::map 과 비슷하게 key-value pair 를 가진다. 다음은 TMap 을 이용한 체스 게임 인터페이스를 구현한 예제 코드이다.
enum class EPieceType
{
King,
Queen,
Rook,
Bishop,
Knight,
Pawn
};
struct FPiece
{
int32 PlayerId;
EPieceType Type;
FIntPoint Position;
FPiece(int32 InPlayerId, EPieceType InType, FIntVector InPosition) :
PlayerId(InPlayerId),
Type(InType),
Position(InPosition)
{
}
};
class FBoard
{
private:
// Using a TMap, we can refer to each piece by its position
TMap<FIntPoint, FPiece> Data;
public:
bool HasPieceAtPosition(FIntPoint Position)
{
return Data.Contains(Position);
}
FPiece GetPieceAtPosition(FIntPoint Position)
{
return Data[Position];
}
void AddNewPiece(int32 PlayerId, EPieceType Type, FIntPoint Position)
{
FPiece NewPiece(PlayerId, Type, Position);
Data.Add(Position, NewPiece);
}
void MovePiece(FIntPoint OldPosition, FIntPoint NewPosition)
{
FPiece Piece = Data[OldPosition];
Piece.Position = NewPosition;
Data.Remove(OldPosition);
Data.Add(NewPosition, Piece);
}
void RemovePieceAtPosition(FIntPoint Position)
{
Data.Remove(Position);
}
void ClearBoard()
{
Data.Empty();
}
};
TSet
TSet 또한 std::set 과 비슷하게 동작한다. 자주 사용하는 API 만 짚고 넘어가자.
TSet<AActor*> ActorSet = GetActorSetFromSomewhere();
int32 Size = ActorSet.Num();
// Adds an element to the set, if the set does not already contain it
AActor* NewActor = GetNewActor();
ActorSet.Add(NewActor);
// Check if an element is already contained by the set
if (ActorSet.Contains(NewActor))
{
// ...
}
// Remove an element from the set
ActorSet.Remove(NewActor);
// Removes all elements from the set
ActorSet.Empty();
// Creates a TArray that contains the elements of your TSet
TArray<AActor*> ActorArrayFromSet = ActorSet.Array();
순회하기 (Iterator, For-each Loop)
Iterator 를 이용해 컨테이너 내부 element 들을 조작할 수 있다.
void RemoveDeadEnemies(TSet<AEnemy*>& EnemySet)
{
// Start at the beginning of the set, and iterate to the end of the set
for (auto EnemyIterator = EnemySet.CreateIterator(); EnemyIterator; ++EnemyIterator)
{
// The * operator gets the current element
AEnemy* Enemy = *EnemyIterator;
if (Enemy.Health == 0)
{
// 'RemoveCurrent' is supported by TSets and TMaps
EnemyIterator.RemoveCurrent();
}
}
}
//////////////////////////
//// iterator 에 관련된 API
//////////////////////////
// Moves the iterator back one element
--EnemyIterator;
// Moves the iterator forward/backward by some offset, where Offset is an integer
EnemyIterator += Offset;
EnemyIterator -= Offset;
// Gets the index of the current element
int32 Index = EnemyIterator.GetIndex();
// Resets the iterator to the first element
EnemyIterator.Reset();
For-each 구문은 c++ 에서 사용했던 것과 동일하게 사용하면 된다.
TSet 이나 TMap 을 활용할 때 custom Hash Function 을 만들 수도 있다. custom class 를 TMap 이나 TSet 에 넣어줄 때는, 해당 Object 의 HashCode 리턴하는 GetTypeHash 함수를 만들어 주어야 한다. 예시 코드를 보자.
class FMyClass
{
uint32 ExampleProperty1;
uint32 ExampleProperty2;
// Hash Function
friend uint32 GetTypeHash(const FMyClass& MyClass)
{
// HashCombine is a utility function for combining two hash values.
uint32 HashCode = HashCombine(MyClass.ExampleProperty1, MyClass.ExampleProperty2);
return HashCode;
}
// For demonstration purposes, two objects that are equal
// should always return the same hash code.
bool operator==(const FMyClass& LHS, const FMyClass& RHS)
{
return LHS.ExampleProperty1 == RHS.ExampleProperty1
&& LHS.ExampleProperty2 == RHS.ExampleProperty2;
}
};
만약 TSet<FMyClass*> 같은 식으로 클래스의 포인터를 붙여서 사용하는 경우에는, 아래와 같은 함수도 같이 만들어 주어야 한다.
friend uint32 GetTypeHash(const FMyClass* MyClass);
'Game Dev > Unreal C++ : Study' 카테고리의 다른 글
[언리얼] LAN 네트워크를 이용한 멀티플레이어 간단 설정 (블루프린트 편) (0) | 2022.05.17 |
---|---|
[언리얼] 멀티플레이어 서버의 이해 (0) | 2022.05.17 |
언리얼 게임플레이 클래스 : UObject, AActor, UActorComponent, UStruct (0) | 2022.04.13 |
언리얼 애니메이션 레이어 (Layered Animation), 본마다 레이어로 블렌딩 (Layered Blend Per Bone) (0) | 2022.04.06 |
언리얼 블렌드 스페이스 (Blendspace) (0) | 2022.04.06 |
Comments