π§° Lesson 2.4: Unreal Core Types β FString, FName, FText & Containers
Unreal has its own strings and containers, and it expects you to use them. They aren't reinventions for their own sake β each solves a real engine need (reflection, performance, localization) that the C++ standard library doesn't address.
π― Learning Objectives
By the end of this lesson, you will be able to:
- Choose correctly between
FString,FName, andFText - Use
TArray,TMap, andTSetfor common gameplay data - Explain why Unreal containers are preferred over
std::vector/std::maphere - Wrap string literals in
TEXT()and understand why - Convert between the string types when you must
Estimated Time: 60 minutes
Engine Version: Unreal Engine 5.8
In This Lesson
Three Strings, Three Jobs
Newcomers are surprised Unreal has three string types. The reason is that "a string" plays three different roles in a game, with different priorities:
| Type | Use it for | Key property |
|---|---|---|
FString | Mutable text you build, manipulate, log | Flexible but heaviest; not for identifiers |
FName | Identifiers: asset names, bone names, tags, keys | Immutable, case-insensitive, hashed β comparison is nearly free |
FText | Anything shown to a player | Localizable β supports translation & culture formatting |
// FString β you're assembling/altering text (e.g. a debug string):
FString DebugLine = FString::Printf(TEXT("Health: %d / %d"), Current, Max);
// FName β a lightweight identifier used as a key or lookup:
FName SocketName = TEXT("hand_r");
if (Mesh->DoesSocketExist(SocketName)) { /* ... */ }
// FText β player-facing, so it can be localized:
UPROPERTY(EditAnywhere, Category = "UI")
FText ButtonLabel = FText::FromString(TEXT("Start Game"));
β The one-line decision
Shown to a player? FText. Used as an identifier/key? FName. Everything else (building, parsing, logging)? FString. Using FName for keys instead of FString is a real performance win β FName comparison is an integer compare, not a character-by-character one.
The TEXT() Macro
You've seen TEXT("...") around every string literal. It's not optional decoration β it forces the literal to be wide (UTF-16) characters, which is what Unreal's string types expect. Omit it and you get needless conversions or subtle bugs.
FString Good = TEXT("Ready"); // wide literal β correct, no conversion
FString Risky = "Ready"; // narrow literal β forces a runtime conversion
UE_LOG(LogTemp, Warning, TEXT("Always wrap log format strings in TEXT()"));
β οΈ Make it a reflex
Wrap every string literal that feeds an Unreal API in TEXT(). It's the norm across the entire engine codebase, and forgetting it is a classic newcomer smell that reviewers will flag immediately.
TArray β the workhorse container
TArray<T> is Unreal's dynamic array β the container you'll reach for most. It's the equivalent of std::vector, but reflection-aware and integrated with the engine.
TArray<AActor*> NearbyEnemies;
NearbyEnemies.Add(SomeEnemy); // append
NearbyEnemies.Num(); // count (not .size())
NearbyEnemies.Contains(SomeEnemy); // membership test
NearbyEnemies.Remove(SomeEnemy); // remove by value
NearbyEnemies[0]; // indexed access
// Range-based for β the idiomatic way to iterate:
for (AActor* Enemy : NearbyEnemies)
{
if (Enemy) { /* always null-check UObject pointers */ }
}
// Reflected TArray shows up as an editable list in the Details panel:
UPROPERTY(EditAnywhere, Category = "Spawning")
TArray<TSubclassOf<AActor>> SpawnableTypes;
π Unreal names, not STL names
Note the API: .Num() not .size(), .Add() not .push_back(), .Contains() not std::find. Same concepts, Unreal spelling. When a TArray is a UPROPERTY, the editor renders it as an add/remove list for free.
TMap & TSet
TMap<Key, Value> stores keyβvalue pairs; TSet<T> stores unique values. Both are hashed for fast lookup.
// TMap: an inventory of item name -> quantity
TMap<FName, int32> Inventory;
Inventory.Add(TEXT("Potion"), 3);
Inventory.Add(TEXT("Key"), 1);
if (int32* Count = Inventory.Find(TEXT("Potion")))
{
// Find returns a pointer to the value, or nullptr if the key is absent.
(*Count)++; // now 4
}
for (const TPair<FName, int32>& Entry : Inventory)
{
UE_LOG(LogTemp, Log, TEXT("%s x%d"), *Entry.Key.ToString(), Entry.Value);
}
// TSet: a collection of unique tags, no duplicates
TSet<FName> ActiveTags;
ActiveTags.Add(TEXT("OnFire"));
ActiveTags.Add(TEXT("OnFire")); // ignored β already present
bool bBurning = ActiveTags.Contains(TEXT("OnFire"));
β οΈ TMap::Find returns a pointer
Inventory.Find(Key) gives you a pointer to the value (or nullptr) β not the value itself. Always check it before dereferencing. This trips people up coming from other languages where map lookup returns a value or throws.
β Pro Tip
FName keys pair beautifully with TMap/TSet because their hashing and comparison are cheap. An Inventory keyed by FName is idiomatic Unreal.
Why Not std::?
You can use std::vector in a .cpp, but Unreal's containers are strongly preferred in gameplay code. The reasons are concrete:
can be UPROPERTY, shown in editor"] A --> S["Serialization:
save/load & replicate"] A --> M["Engine memory allocators & profiling"] A --> D["First-class debugger & log support"] B["std:: containers"] --> N["None of the above out of the box"]
std::vector can't be a UPROPERTY.π‘ The practical line: if the data is a member of a reflected class, needs to appear in the editor, be saved, or be replicated, it must be an Unreal container. For a purely local, throwaway computation inside one function, either works β but staying consistent with Unreal's types keeps the codebase uniform.
Hands-on Exercise & Quiz
ποΈ Exercise: Pick the type
Objective: Match each piece of data to the right Unreal type.
- The label on a quest button, shown to players in multiple languages
- The key identifying which weapon socket to attach to
- A list of all pickups currently overlapping the player
- A mapping from ability name to its cooldown remaining
- A debug message you assemble from several numbers
β Answers
- 1.
FTextβ player-facing, localizable. - 2.
FNameβ an identifier/key, compared cheaply. - 3.
TArray<AActor*>(or of your pickup type) β an ordered, growable list. - 4.
TMap<FName, float>β keyβvalue lookup. - 5.
FStringviaFString::Printf(TEXT("..."), ...)β mutable, built text.
π― Quick Quiz
Question 1: Which type should hold text displayed to the player?
Question 2: What does TMap::Find(Key) return when the key is missing?
Question 3: Why can't a std::vector member be a UPROPERTY?
Summary
π Key Takeaways
- Three strings, three jobs:
FTextfor player-facing/localizable text,FNamefor cheap identifiers/keys,FStringfor mutable/built text. - Wrap every string literal feeding an Unreal API in
TEXT(). TArray(list),TMap(keyβvalue), andTSet(unique values) are the core containers β note Unreal's API names (.Num(),.Add(),.Contains()).TMap::Findreturns a pointer (nullptr if absent) β always check it.- Prefer Unreal containers in gameplay code: only they can be
UPROPERTYs and plug into the editor, serialization, and networking.
π Additional Resources
π What's Next?
We keep hinting at garbage collection and "keeping objects alive." Time to face it directly: the memory model β the UObject lifecycle, how the GC decides what lives, TObjectPtr, and the smart pointers you use for non-UObjects.
π Lesson complete!
You've got Unreal's vocabulary. Now, its memory.