Arrays, Sets, Maps, and ForEach¶
Containers are how a Blueprint remembers zero, one, or many things. Most container bugs are not "Unreal being weird"; they are the graph asking for an item that is not in the container, or choosing the wrong container shape for the job.
The one-minute version¶
- An Array is an ordered list of one type. It can be empty, it can contain
duplicates, and its first index is
0. - A Set is an unordered collection of unique values of one type. It is good for "who is currently inside this trigger?" because adding the same actor twice still leaves one entry.
- A Map stores key-value pairs. The key is how you find the value later: item ID -> item count, surface type -> footstep sound, checkpoint name -> spawn data.
- For Each Loop runs once per element in a container snapshot. It does not make invalid object references safe, and a huge loop still costs real frame time.
- Empty containers are normal. Branch on
Length,Contains, orFindresult before assuming an item exists. - Use containers to model the data you actually need, not as a way to avoid deciding who owns the data.
What each container stores¶
All three Blueprint containers are typed. An array of BP_Door references only
stores BP_Door references. A map from Name to Integer only accepts those
key and value types.
| Container | Shape | Allows duplicates? | Order matters? | Beginner use |
|---|---|---|---|---|
| Array | Index -> Value |
Yes | Yes | Inventory slots, ordered patrol points, trace results, all actors returned by a search. |
| Set | Value |
No | No stable order to rely on | Current overlap members, unlocked abilities, actors already processed this pulse. |
| Map | Key -> Value |
Keys no, values yes | No stable order to rely on | Item counts by ID, widget references by slot name, tuning values by tag/name. |
Array indices are positions. Map keys are labels. Set entries are their own labels.
Array InventorySlots:
[0] Potion
[1] Potion
[2] Key
Set OverlappingActors:
BP_PlayerCharacter
BP_AICompanion
Map ItemCounts:
Potion -> 2
Key -> 1
That difference matters when a bug happens. "Index out of bounds" is an array position problem. "Find returned false" is a map key problem. "Add did not increase count" may be a set doing its job by refusing a duplicate.
Arrays¶
Use an array when the position in the list matters, or when you naturally want to walk through every item.
Common beginner arrays:
- waypoints in patrol order;
- inventory slots shown left to right;
- actors returned by
Get All Actors Of Class; - recent damage numbers;
- hit results from a multi trace.
The empty array is not an error:
Get All Actors Of Class BP_Enemy
-> Out Actors = [] when no enemies exist
-> Length = 0
The dangerous part is indexing without checking:
Bad:
Out Actors -> Get index 0 -> call AttackPlayer
Safer:
Out Actors -> Length > 0
true -> Get index 0
false -> no enemy found path
Blueprint arrays are zero-based:
Length = 3
valid indices = 0, 1, 2
last valid index = Length - 1
If you get an out-of-bounds warning, do not guard it with Is Valid first.
The problem is that the list does not contain an element at that index.
Sets¶
Use a set when uniqueness is the point and order is not.
A trigger volume that tracks actors inside it is the classic example:
On Component Begin Overlap
-> Other Actor passes filter
-> Add Other Actor to CurrentOverlappers set
On Component End Overlap
-> Remove Other Actor from CurrentOverlappers set
CanOpenDoor:
return CurrentOverlappers contains PlayerCharacter
An array can also do this, but arrays make duplicates your problem. If two components on the same actor overlap the trigger, a naive array can receive the same actor more than once. A set says "this actor is either in the group or it is not."
Sets are not sorted lists. If you need "first", "last", "next", or a stable display order, convert deliberately or use an array with explicit sorting.
Maps¶
Use a map when you have a reliable key and want the matching value.
Map ItemCounts:
Key = ItemId (Name)
Value = Count (Integer)
Pickup Potion:
if ItemCounts contains Potion:
ItemCounts[Potion] = ItemCounts[Potion] + 1
else:
Add Potion -> 1
Good keys are stable identifiers:
Nameitem IDs such asPotion_Small;- Gameplay Tags such as
Surface.StoneorWeapon.Rifle; - enum values such as
EDamageType::Fire; - object references when the object lifetime is clearly owned elsewhere.
Bad keys are values that drift or are not unique:
- display text the player sees;
- actor names generated at runtime;
- floats from measurements;
- object references that can be destroyed without cleanup.
Maps return "found or not found" style answers. Treat a missing key as a real branch, not an exceptional crash:
Find ItemCounts by ItemId
-> Found?
true: update existing count
false: add default count
For Each Loop¶
For Each Loop takes a container and runs the loop body once for each element.
For arrays, it gives the current element and array index. For sets/maps, the
available pins depend on the container node you are using.
For Each Loop Enemies
-> Array Element
-> Is Valid
-> Apply Damage
The loop is still one execution path in the current frame. If you run a loop over 500 actors from Tick, that is 500 iterations every frame. The node is convenient; it is not free.
Be careful when changing the container you are looping over. The beginner-safe pattern is to collect changes separately, then apply them after the loop:
For Each CurrentOverlappers
-> if actor invalid:
add actor to PendingRemoval
For Each PendingRemoval
-> remove from CurrentOverlappers
When containers fail¶
Containers usually fail by giving you an empty result, a warning, or stale references.
| Symptom | Likely cause | Fix shape |
|---|---|---|
Accessed None from an array element |
The element is an object reference that is empty or destroyed. | Is Valid each element before calling into it. |
| Array index warning | The index is outside 0..Length - 1. |
Check Length, clamp, or use For Each instead of manual indices. |
| A set "does not add" the second copy | Sets keep unique values. | Use an array if duplicates are meaningful, or store a count in a map. |
| Map lookup returns not found | The key was never added, changed, or is not the same type/value. | Branch on Find/Contains and add a default path. |
| Loop sees old actors | You stored object references and the actors were destroyed. | Remove on EndOverlap/EndPlay and guard with Is Valid. |
| Order changes unexpectedly | You used a set or map where display order matters. | Use an array or build a sorted array for display. |
What containers do not do¶
- They do not create the actors, widgets, or assets they reference.
- They do not keep an actor alive after it is destroyed.
- They do not replicate just because the variable is a container. Replication still needs the owning actor and variable settings to be correct.
- They do not save themselves to disk unless you copy the values into a SaveGame object.
- They do not make a bad ownership model good. Decide whether the character, controller, component, GameInstance, or SaveGame owns the data.
The pattern everyone actually uses¶
For a door that opens while any player is inside:
Variable CurrentUsers: Set of BP_PlayerCharacter references
BeginOverlap:
Other Actor -> Cast/Interface filter
Add player to CurrentUsers
if CurrentUsers length > 0:
OpenDoor
EndOverlap:
Remove player from CurrentUsers
if CurrentUsers length == 0:
CloseDoor
For a small inventory:
Struct FInventoryEntry:
ItemId (Name)
DisplayName (Text)
Count (Integer)
Array InventorySlots for UI order
Map ItemIdToSlotIndex for quick lookup
Start with one container. Add the second only when the graph proves you need both display order and fast lookup.
Lookalikes - which one do I want?¶
| Tool | Use when | Watch out |
|---|---|---|
| Array | You need ordered zero/one/many values of one type. | Check Length before Get by index. |
| Set | You need unique membership and do not care about order. | Do not rely on iteration order for UI. |
| Map | You need to find a value by a stable key. | Missing keys are normal; branch on Find/Contains. |
| Struct | Several fields should travel as one value. | A struct is one value, not a list. |
| Data Table | Many rows of the same struct belong in data. | Rows need a struct shape and stable row names. |
| Data Asset | One authored asset should hold a chunk of reusable data. | Asset references are not live actor references. |
Rule of thumb: array for order, set for uniqueness, map for lookup.
Going deeper¶
- Blueprint variables and references - why a typed object reference can still be empty.
- The Blueprint survival kit - functions, events, structs, enums, and containers as graph organization tools.
- Accessed None trying to read property - debugging empty object references inside containers.
- Get Actor Of Class / Get All Actors Of Class - why actor searches return arrays and why repeated searches are usually the wrong ownership model.
- Official docs: Basic Scripting with Blueprints, Blueprint Arrays, Blueprint Maps, Working with Sets, and Flow Control.
- Engine source (requires engine access - we do not reproduce it here):
Blueprint container helpers are declared in
UKismetArrayLibrary,UKismetSetLibrary, andUKismetMapLibraryunderEngine/Source/Runtime/Engine/Classes/Kismet/; loop and container expansion nodes live underEngine/Source/Editor/BlueprintGraph/Classes/.