claude-code-arcane 2.3.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +54 -6
- package/agents/game/ue-blueprint-specialist.md +125 -0
- package/agents/game/ue-gas-specialist.md +133 -0
- package/agents/game/ue-umg-specialist.md +131 -0
- package/dist/cli.js +5 -0
- package/docs/SKILLS-CATALOG.md +42 -3
- package/docs/USER-GUIDE.md +2 -2
- package/hooks/detect-unreal.sh +90 -0
- package/package.json +1 -1
- package/profiles/unreal-dev.yaml +97 -0
- package/rules/gamedev/unreal-code.md +125 -0
- package/skills/install-mcp/SKILL.md +135 -46
- package/skills/install-mcp/references/unreal-operations.md +55 -0
- package/skills/ue-actor-component-architecture/SKILL.md +509 -0
- package/skills/ue-actor-component-architecture/references/actor-lifecycle.md +301 -0
- package/skills/ue-actor-component-architecture/references/component-types.md +592 -0
- package/skills/ue-ai-navigation/SKILL.md +526 -0
- package/skills/ue-ai-navigation/references/behavior-tree-patterns.md +392 -0
- package/skills/ue-ai-navigation/references/behavior-tree-workflow.md +107 -0
- package/skills/ue-ai-navigation/references/custom-bttask.md +104 -0
- package/skills/ue-ai-navigation/references/eqs-reference.md +423 -0
- package/skills/ue-animation-system/SKILL.md +521 -0
- package/skills/ue-animation-system/references/anim-notify-reference.md +486 -0
- package/skills/ue-animation-system/references/locomotion-setup.md +550 -0
- package/skills/ue-async-threading/SKILL.md +433 -0
- package/skills/ue-async-threading/references/thread-safety-guide.md +213 -0
- package/skills/ue-async-threading/references/threading-patterns.md +345 -0
- package/skills/ue-audio-system/SKILL.md +527 -0
- package/skills/ue-audio-system/references/audio-setup-patterns.md +700 -0
- package/skills/ue-blueprints/SKILL.md +134 -0
- package/skills/ue-blueprints/references/communication.md +82 -0
- package/skills/ue-character-movement/SKILL.md +475 -0
- package/skills/ue-character-movement/references/cmc-extension-patterns.md +335 -0
- package/skills/ue-character-movement/references/movement-pipeline.md +189 -0
- package/skills/ue-cpp-foundations/SKILL.md +533 -0
- package/skills/ue-cpp-foundations/references/components-and-gc.md +107 -0
- package/skills/ue-cpp-foundations/references/container-patterns.md +544 -0
- package/skills/ue-cpp-foundations/references/delegate-patterns.md +497 -0
- package/skills/ue-cpp-foundations/references/gameplay-classes-cpp.md +136 -0
- package/skills/ue-cpp-foundations/references/property-specifiers.md +306 -0
- package/skills/ue-data-assets-tables/SKILL.md +527 -0
- package/skills/ue-data-assets-tables/references/asset-loading-patterns.md +306 -0
- package/skills/ue-data-assets-tables/references/data-driven-design.md +378 -0
- package/skills/ue-editor-tools/SKILL.md +523 -0
- package/skills/ue-editor-tools/references/detail-customization-patterns.md +395 -0
- package/skills/ue-editor-tools/references/editor-module-setup.md +480 -0
- package/skills/ue-game-features/SKILL.md +477 -0
- package/skills/ue-game-features/references/experience-system.md +222 -0
- package/skills/ue-game-features/references/game-feature-patterns.md +308 -0
- package/skills/ue-gameplay-abilities/SKILL.md +518 -0
- package/skills/ue-gameplay-abilities/references/ability-task-reference.md +391 -0
- package/skills/ue-gameplay-abilities/references/gameplay-effect-reference.md +397 -0
- package/skills/ue-gameplay-abilities/references/gas-setup-patterns.md +369 -0
- package/skills/ue-gameplay-framework/SKILL.md +466 -0
- package/skills/ue-gameplay-framework/references/framework-class-map.md +243 -0
- package/skills/ue-input-system/SKILL.md +394 -0
- package/skills/ue-input-system/references/cpp-setup.md +131 -0
- package/skills/ue-input-system/references/enhanced-input-workflow.md +117 -0
- package/skills/ue-input-system/references/input-action-reference.md +731 -0
- package/skills/ue-mass-entity/SKILL.md +476 -0
- package/skills/ue-mass-entity/references/mass-entity-patterns.md +319 -0
- package/skills/ue-mass-entity/references/mass-fragment-reference.md +119 -0
- package/skills/ue-materials-rendering/SKILL.md +526 -0
- package/skills/ue-materials-rendering/references/material-parameter-reference.md +216 -0
- package/skills/ue-materials-rendering/references/post-process-settings.md +383 -0
- package/skills/ue-module-build-system/SKILL.md +487 -0
- package/skills/ue-module-build-system/references/build-cs-reference.md +418 -0
- package/skills/ue-module-build-system/references/common-build-errors.md +381 -0
- package/skills/ue-naming-conventions/SKILL.md +142 -0
- package/skills/ue-naming-conventions/references/asset-name-modifiers.md +233 -0
- package/skills/ue-naming-conventions/references/content-structure.md +83 -0
- package/skills/ue-networking-replication/SKILL.md +452 -0
- package/skills/ue-networking-replication/references/replication-patterns.md +526 -0
- package/skills/ue-networking-replication/references/rpc-decision-guide.md +393 -0
- package/skills/ue-niagara-effects/SKILL.md +531 -0
- package/skills/ue-niagara-effects/references/niagara-data-interfaces.md +491 -0
- package/skills/ue-niagara-effects/references/niagara-parameter-types.md +153 -0
- package/skills/ue-niagara-effects/references/niagara-workflow.md +115 -0
- package/skills/ue-packaging/SKILL.md +116 -0
- package/skills/ue-physics-collision/SKILL.md +423 -0
- package/skills/ue-physics-collision/references/collision-channel-setup.md +218 -0
- package/skills/ue-physics-collision/references/trace-patterns.md +499 -0
- package/skills/ue-procedural-generation/SKILL.md +505 -0
- package/skills/ue-procedural-generation/references/pcg-node-reference.md +381 -0
- package/skills/ue-procedural-generation/references/procedural-mesh-patterns.md +940 -0
- package/skills/ue-project-context/SKILL.md +305 -0
- package/skills/ue-sequencer-cinematics/SKILL.md +506 -0
- package/skills/ue-sequencer-cinematics/references/sequencer-patterns.md +629 -0
- package/skills/ue-serialization-savegames/SKILL.md +524 -0
- package/skills/ue-serialization-savegames/references/save-system-architecture.md +705 -0
- package/skills/ue-state-trees/SKILL.md +489 -0
- package/skills/ue-state-trees/references/state-tree-mass-integration.md +184 -0
- package/skills/ue-state-trees/references/state-tree-patterns.md +321 -0
- package/skills/ue-testing-debugging/SKILL.md +520 -0
- package/skills/ue-testing-debugging/references/automation-test-patterns.md +387 -0
- package/skills/ue-testing-debugging/references/profiling-commands.md +357 -0
- package/skills/ue-ui-umg-slate/SKILL.md +431 -0
- package/skills/ue-ui-umg-slate/references/common-ui-setup.md +484 -0
- package/skills/ue-ui-umg-slate/references/widget-types.md +487 -0
- package/skills/ue-world-level-streaming/SKILL.md +518 -0
- package/skills/ue-world-level-streaming/references/streaming-patterns.md +414 -0
- package/templates/gamedev/unreal-project-context.md +107 -0
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
# Container Patterns Reference
|
|
2
|
+
|
|
3
|
+
Detailed patterns, performance notes, and advanced usage for TArray, TMap, TSet, TOptional, TVariant, and related UE containers.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## TArray
|
|
8
|
+
|
|
9
|
+
`TArray<T, AllocatorType>` is defined in `Engine/Source/Runtime/Core/Public/Containers/Array.h`. It stores elements in a contiguous block of memory and is the default container for ordered sequences.
|
|
10
|
+
|
|
11
|
+
### Core Operations
|
|
12
|
+
|
|
13
|
+
```cpp
|
|
14
|
+
TArray<int32> Numbers;
|
|
15
|
+
|
|
16
|
+
// Construction
|
|
17
|
+
TArray<int32> FromInit = { 1, 2, 3, 4, 5 };
|
|
18
|
+
TArray<int32> Copy = FromInit;
|
|
19
|
+
TArray<int32> Moved = MoveTemp(FromInit); // Move — FromInit is empty after
|
|
20
|
+
|
|
21
|
+
// Capacity management
|
|
22
|
+
Numbers.Reserve(64); // Pre-allocate without changing Num()
|
|
23
|
+
Numbers.SetNum(10); // Resize, default-initializes new elements
|
|
24
|
+
Numbers.SetNumZeroed(10); // Resize, zero-initializes new elements
|
|
25
|
+
Numbers.SetNumUninitialized(10); // Resize without initialization (POD only)
|
|
26
|
+
Numbers.Shrink(); // Release excess capacity
|
|
27
|
+
Numbers.Empty(); // Clear all elements, free allocation
|
|
28
|
+
Numbers.Reset(); // Clear all elements, keep capacity (no reallocation)
|
|
29
|
+
Numbers.Empty(64); // Clear + hint future capacity
|
|
30
|
+
|
|
31
|
+
// Element count
|
|
32
|
+
int32 Count = Numbers.Num();
|
|
33
|
+
bool bEmpty = Numbers.IsEmpty();
|
|
34
|
+
int32 MaxCap = Numbers.Max(); // Current allocation capacity
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Adding Elements
|
|
38
|
+
|
|
39
|
+
```cpp
|
|
40
|
+
TArray<FString> Names;
|
|
41
|
+
|
|
42
|
+
// Add — copies or moves
|
|
43
|
+
Names.Add(TEXT("Alpha"));
|
|
44
|
+
Names.Add(FString(TEXT("Beta"))); // Move from rvalue
|
|
45
|
+
|
|
46
|
+
// Emplace — constructs in-place (avoids copy/move overhead for complex types)
|
|
47
|
+
Names.Emplace(TEXT("Gamma"));
|
|
48
|
+
|
|
49
|
+
// AddUnique — O(n) search before add; use TSet if uniqueness is the main goal
|
|
50
|
+
Names.AddUnique(TEXT("Alpha")); // No-op: already present
|
|
51
|
+
|
|
52
|
+
// Insert at index (shifts all subsequent elements)
|
|
53
|
+
Names.Insert(TEXT("First"), 0);
|
|
54
|
+
|
|
55
|
+
// Append
|
|
56
|
+
TArray<FString> Extra = { TEXT("X"), TEXT("Y") };
|
|
57
|
+
Names.Append(Extra);
|
|
58
|
+
Names.Append({ TEXT("P"), TEXT("Q") }); // Initializer list
|
|
59
|
+
|
|
60
|
+
// Push/Pop (stack semantics)
|
|
61
|
+
Names.Push(TEXT("Top"));
|
|
62
|
+
FString Top = Names.Pop(); // Removes and returns last element
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Removal
|
|
66
|
+
|
|
67
|
+
```cpp
|
|
68
|
+
TArray<int32> V = { 1, 2, 3, 2, 4, 2 };
|
|
69
|
+
|
|
70
|
+
// Remove — removes all occurrences; order-preserving; O(n)
|
|
71
|
+
int32 Removed = V.Remove(2); // Removed == 3
|
|
72
|
+
|
|
73
|
+
// RemoveSwap — removes all; swaps with last; O(1) per removal but destroys order
|
|
74
|
+
V.RemoveSwap(3);
|
|
75
|
+
|
|
76
|
+
// RemoveAt — by index; order-preserving; O(n)
|
|
77
|
+
V.RemoveAt(0);
|
|
78
|
+
|
|
79
|
+
// RemoveAtSwap — by index; swaps with last; O(1); destroys order
|
|
80
|
+
V.RemoveAtSwap(0);
|
|
81
|
+
|
|
82
|
+
// RemoveSingle — removes only the first occurrence
|
|
83
|
+
V.RemoveSingle(4);
|
|
84
|
+
|
|
85
|
+
// RemoveAll — removes all matching a predicate
|
|
86
|
+
V.RemoveAll([](int32 N) { return N % 2 == 0; });
|
|
87
|
+
|
|
88
|
+
// RemoveAllSwap — same but unordered (faster)
|
|
89
|
+
V.RemoveAllSwap([](int32 N) { return N < 0; });
|
|
90
|
+
|
|
91
|
+
// Pop — removes the last element
|
|
92
|
+
if (V.Num() > 0)
|
|
93
|
+
{
|
|
94
|
+
int32 Last = V.Pop();
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Search & Query
|
|
99
|
+
|
|
100
|
+
```cpp
|
|
101
|
+
TArray<FString> Names = { TEXT("Alpha"), TEXT("Beta"), TEXT("Gamma") };
|
|
102
|
+
|
|
103
|
+
// Find — returns INDEX_NONE if not found
|
|
104
|
+
int32 Idx = Names.Find(TEXT("Beta")); // 1
|
|
105
|
+
int32 Rev = Names.FindLast(TEXT("Beta")); // 1 (from end)
|
|
106
|
+
|
|
107
|
+
// Contains — bool
|
|
108
|
+
bool bHas = Names.Contains(TEXT("Delta")); // false
|
|
109
|
+
|
|
110
|
+
// FindByPredicate — returns pointer to first match or nullptr
|
|
111
|
+
FString* Match = Names.FindByPredicate([](const FString& S)
|
|
112
|
+
{
|
|
113
|
+
return S.StartsWith(TEXT("G"));
|
|
114
|
+
});
|
|
115
|
+
// Use as: if (Match) { ... }
|
|
116
|
+
|
|
117
|
+
// IndexOfByPredicate — returns index or INDEX_NONE
|
|
118
|
+
int32 GIdx = Names.IndexOfByPredicate([](const FString& S)
|
|
119
|
+
{
|
|
120
|
+
return S.Contains(TEXT("amm"));
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// ContainsByPredicate
|
|
124
|
+
bool bAnyLong = Names.ContainsByPredicate([](const FString& S)
|
|
125
|
+
{
|
|
126
|
+
return S.Len() > 4;
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// FilterByPredicate — returns a new TArray
|
|
130
|
+
TArray<FString> Long = Names.FilterByPredicate([](const FString& S)
|
|
131
|
+
{
|
|
132
|
+
return S.Len() > 4;
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// IsValidIndex
|
|
136
|
+
bool bValid = Names.IsValidIndex(5); // false
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Sorting
|
|
140
|
+
|
|
141
|
+
```cpp
|
|
142
|
+
TArray<int32> Numbers = { 5, 3, 1, 4, 2 };
|
|
143
|
+
|
|
144
|
+
// Sort with operator< (modifies in-place)
|
|
145
|
+
Numbers.Sort();
|
|
146
|
+
|
|
147
|
+
// Sort with predicate
|
|
148
|
+
Numbers.Sort([](int32 A, int32 B) { return A > B; }); // Descending
|
|
149
|
+
|
|
150
|
+
// StableSort — preserves relative order of equal elements
|
|
151
|
+
Numbers.StableSort([](int32 A, int32 B) { return A < B; });
|
|
152
|
+
|
|
153
|
+
// Sort array of pointers/objects by a member
|
|
154
|
+
TArray<FWeaponStats*> Weapons;
|
|
155
|
+
Weapons.Sort([](const FWeaponStats* A, const FWeaponStats* B)
|
|
156
|
+
{
|
|
157
|
+
return A->BaseDamage > B->BaseDamage;
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// HeapSort (O(n log n), not stable, slightly faster than Sort)
|
|
161
|
+
Numbers.HeapSort();
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Iteration Patterns
|
|
165
|
+
|
|
166
|
+
```cpp
|
|
167
|
+
TArray<AActor*> Actors;
|
|
168
|
+
|
|
169
|
+
// Ranged-for (do NOT add/remove during iteration)
|
|
170
|
+
for (AActor* Actor : Actors)
|
|
171
|
+
{
|
|
172
|
+
if (IsValid(Actor)) { Actor->Destroy(); }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Indexed (safe for removal when iterating backwards)
|
|
176
|
+
for (int32 i = Actors.Num() - 1; i >= 0; --i)
|
|
177
|
+
{
|
|
178
|
+
if (!IsValid(Actors[i]))
|
|
179
|
+
{
|
|
180
|
+
Actors.RemoveAtSwap(i);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Iterator with inline removal
|
|
185
|
+
for (auto It = Actors.CreateIterator(); It; ++It)
|
|
186
|
+
{
|
|
187
|
+
if (!IsValid(*It))
|
|
188
|
+
{
|
|
189
|
+
It.RemoveCurrent();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Const iterator
|
|
194
|
+
for (auto It = Actors.CreateConstIterator(); It; ++It)
|
|
195
|
+
{
|
|
196
|
+
UE_LOG(LogTemp, Log, TEXT("%s"), *(*It)->GetName());
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Memory & Performance Notes
|
|
201
|
+
|
|
202
|
+
- `Add` / `Emplace` are O(1) amortized; worst-case O(n) when reallocation occurs.
|
|
203
|
+
- `Remove` / `RemoveAt` are O(n) — they shift elements. Use `RemoveSwap` / `RemoveAtSwap` for O(1) when order does not matter.
|
|
204
|
+
- `Reserve` before filling large arrays eliminates reallocation churn.
|
|
205
|
+
- Use `Emplace` over `Add` for non-trivial types to avoid an extra copy.
|
|
206
|
+
- `SetNumUninitialized` is the fastest way to resize for POD bulk-fill patterns.
|
|
207
|
+
- Avoid `Contains` / `Find` in hot paths on large arrays — O(n). Use `TSet` or `TMap` instead.
|
|
208
|
+
|
|
209
|
+
### Fixed-Size Inline Allocation
|
|
210
|
+
|
|
211
|
+
```cpp
|
|
212
|
+
// TArray with inline storage for up to N elements (avoids heap for small arrays)
|
|
213
|
+
TArray<int32, TInlineAllocator<8>> SmallList;
|
|
214
|
+
SmallList.Add(1); // Stored inline; no heap alloc if <= 8 elements
|
|
215
|
+
|
|
216
|
+
// Fixed max size (compile-time assertion on overflow)
|
|
217
|
+
TArray<int32, TFixedAllocator<16>> FixedList;
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## TMap
|
|
223
|
+
|
|
224
|
+
`TMap<K, V>` is a hash map backed by a sparse array. Average O(1) lookup, insertion, and removal. Defined in `Engine/Source/Runtime/Core/Public/Containers/Map.h`.
|
|
225
|
+
|
|
226
|
+
### Core Operations
|
|
227
|
+
|
|
228
|
+
```cpp
|
|
229
|
+
TMap<FName, float> WeaponDamage;
|
|
230
|
+
|
|
231
|
+
// Add — overwrites if key exists
|
|
232
|
+
WeaponDamage.Add(FName("Rifle"), 35.f);
|
|
233
|
+
WeaponDamage.Add(FName("Shotgun"), 80.f);
|
|
234
|
+
|
|
235
|
+
// Emplace — constructs value in-place
|
|
236
|
+
WeaponDamage.Emplace(FName("Pistol"), 20.f);
|
|
237
|
+
|
|
238
|
+
// FindOrAdd — returns ref to existing or newly-default-inserted value
|
|
239
|
+
float& RifleRef = WeaponDamage.FindOrAdd(FName("Rifle")); // Returns 35.f ref
|
|
240
|
+
float& SniperRef = WeaponDamage.FindOrAdd(FName("Sniper")); // Inserts 0.f, returns ref
|
|
241
|
+
SniperRef = 120.f;
|
|
242
|
+
|
|
243
|
+
// Find — returns pointer; nullptr if absent
|
|
244
|
+
float* DamagePtr = WeaponDamage.Find(FName("Rifle"));
|
|
245
|
+
if (DamagePtr)
|
|
246
|
+
{
|
|
247
|
+
*DamagePtr *= 1.5f; // Modify in-place
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// FindRef — returns value copy (or default if absent) — no way to distinguish "absent" from "zero"
|
|
251
|
+
float GrenadeDmg = WeaponDamage.FindRef(FName("Grenade")); // 0.f (not found)
|
|
252
|
+
|
|
253
|
+
// Contains
|
|
254
|
+
bool bHasRifle = WeaponDamage.Contains(FName("Rifle")); // true
|
|
255
|
+
|
|
256
|
+
// Remove
|
|
257
|
+
int32 NumRemoved = WeaponDamage.Remove(FName("Pistol")); // 1
|
|
258
|
+
|
|
259
|
+
// Num / IsEmpty
|
|
260
|
+
int32 Count = WeaponDamage.Num();
|
|
261
|
+
bool bEmpty = WeaponDamage.IsEmpty();
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Iteration
|
|
265
|
+
|
|
266
|
+
```cpp
|
|
267
|
+
TMap<FName, int32> ItemCounts;
|
|
268
|
+
|
|
269
|
+
// Iterate all pairs
|
|
270
|
+
for (const TPair<FName, int32>& Pair : ItemCounts)
|
|
271
|
+
{
|
|
272
|
+
UE_LOG(LogTemp, Log, TEXT("%s = %d"), *Pair.Key.ToString(), Pair.Value);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Structured bindings (C++17)
|
|
276
|
+
for (auto& [Key, Value] : ItemCounts)
|
|
277
|
+
{
|
|
278
|
+
Value += 10;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Keys only
|
|
282
|
+
TArray<FName> Keys;
|
|
283
|
+
ItemCounts.GetKeys(Keys);
|
|
284
|
+
|
|
285
|
+
// Values only
|
|
286
|
+
TArray<int32> Values;
|
|
287
|
+
ItemCounts.GenerateValueArray(Values);
|
|
288
|
+
|
|
289
|
+
// Key-value arrays
|
|
290
|
+
TArray<FName> OutKeys;
|
|
291
|
+
TArray<int32> OutValues;
|
|
292
|
+
ItemCounts.GenerateKeyArray(OutKeys);
|
|
293
|
+
ItemCounts.GenerateValueArray(OutValues);
|
|
294
|
+
|
|
295
|
+
// Iterator with removal
|
|
296
|
+
for (auto It = ItemCounts.CreateIterator(); It; ++It)
|
|
297
|
+
{
|
|
298
|
+
if (It.Value() <= 0)
|
|
299
|
+
{
|
|
300
|
+
It.RemoveCurrent();
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Memory & Performance Notes
|
|
306
|
+
|
|
307
|
+
- TMap uses a hash table with open addressing on a sparse array. Iteration visits only live pairs (no gaps for the user).
|
|
308
|
+
- Keys must implement `GetTypeHash()` and `operator==`. FName, FString, int32, FGuid, etc. are supported out of the box.
|
|
309
|
+
- `Reserve` before bulk-adding: `WeaponDamage.Reserve(64)`.
|
|
310
|
+
- `TMap::Empty(Slack)` clears and reserves `Slack` slots.
|
|
311
|
+
- For read-heavy maps that rarely change, prefer building once then querying.
|
|
312
|
+
- `TMultiMap<K, V>` allows duplicate keys — access with `MultiFind()`.
|
|
313
|
+
|
|
314
|
+
### Custom Key Hashing
|
|
315
|
+
|
|
316
|
+
```cpp
|
|
317
|
+
// For custom struct keys, provide GetTypeHash and operator==
|
|
318
|
+
struct FItemKey
|
|
319
|
+
{
|
|
320
|
+
FName Category;
|
|
321
|
+
int32 Tier;
|
|
322
|
+
|
|
323
|
+
bool operator==(const FItemKey& Other) const
|
|
324
|
+
{
|
|
325
|
+
return Category == Other.Category && Tier == Other.Tier;
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
inline uint32 GetTypeHash(const FItemKey& Key)
|
|
330
|
+
{
|
|
331
|
+
return HashCombine(GetTypeHash(Key.Category), GetTypeHash(Key.Tier));
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
TMap<FItemKey, float> ItemValues;
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## TSet
|
|
340
|
+
|
|
341
|
+
`TSet<T>` is a hash set — unique elements, O(1) average operations. Backed by the same sparse array as TMap (without values).
|
|
342
|
+
|
|
343
|
+
### Core Operations
|
|
344
|
+
|
|
345
|
+
```cpp
|
|
346
|
+
TSet<FName> Tags;
|
|
347
|
+
|
|
348
|
+
// Add
|
|
349
|
+
Tags.Add(FName("Flying"));
|
|
350
|
+
Tags.Add(FName("Aquatic"));
|
|
351
|
+
Tags.Add(FName("Flying")); // No-op — already present
|
|
352
|
+
|
|
353
|
+
// Contains
|
|
354
|
+
bool bFlying = Tags.Contains(FName("Flying")); // true
|
|
355
|
+
|
|
356
|
+
// Remove
|
|
357
|
+
Tags.Remove(FName("Aquatic"));
|
|
358
|
+
|
|
359
|
+
// Num / IsEmpty
|
|
360
|
+
int32 Count = Tags.Num();
|
|
361
|
+
|
|
362
|
+
// Reserve
|
|
363
|
+
Tags.Reserve(32);
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Set Operations
|
|
367
|
+
|
|
368
|
+
```cpp
|
|
369
|
+
TSet<FName> A = { FName("Fire"), FName("Ice"), FName("Wind") };
|
|
370
|
+
TSet<FName> B = { FName("Ice"), FName("Wind"), FName("Earth") };
|
|
371
|
+
|
|
372
|
+
TSet<FName> Intersection = A.Intersect(B); // { Ice, Wind }
|
|
373
|
+
TSet<FName> Union = A.Union(B); // { Fire, Ice, Wind, Earth }
|
|
374
|
+
TSet<FName> Difference = A.Difference(B); // { Fire }
|
|
375
|
+
|
|
376
|
+
bool bSubset = B.Includes(A); // false (A has Fire which B doesn't)
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Iteration
|
|
380
|
+
|
|
381
|
+
```cpp
|
|
382
|
+
TSet<FName> Tags;
|
|
383
|
+
|
|
384
|
+
for (const FName& Tag : Tags)
|
|
385
|
+
{
|
|
386
|
+
UE_LOG(LogTemp, Log, TEXT("Tag: %s"), *Tag.ToString());
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Iterator with removal
|
|
390
|
+
for (auto It = Tags.CreateIterator(); It; ++It)
|
|
391
|
+
{
|
|
392
|
+
if (It->IsNone())
|
|
393
|
+
{
|
|
394
|
+
It.RemoveCurrent();
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## TOptional
|
|
402
|
+
|
|
403
|
+
`TOptional<T>` represents a value that may or may not be present. Avoids sentinel values like `-1` or `nullptr`.
|
|
404
|
+
|
|
405
|
+
```cpp
|
|
406
|
+
TOptional<int32> MaybeLevel;
|
|
407
|
+
|
|
408
|
+
// Check and access
|
|
409
|
+
if (MaybeLevel.IsSet())
|
|
410
|
+
{
|
|
411
|
+
int32 Level = MaybeLevel.GetValue(); // Asserts if not set
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Safe access with default
|
|
415
|
+
int32 Level = MaybeLevel.Get(1); // Returns 1 if not set
|
|
416
|
+
|
|
417
|
+
// Set
|
|
418
|
+
MaybeLevel = 5;
|
|
419
|
+
|
|
420
|
+
// Reset (clear)
|
|
421
|
+
MaybeLevel.Reset();
|
|
422
|
+
|
|
423
|
+
// IsSet shorthand in conditions
|
|
424
|
+
if (MaybeLevel.IsSet() && MaybeLevel.GetValue() > 10)
|
|
425
|
+
{
|
|
426
|
+
// ...
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Return from function
|
|
430
|
+
TOptional<FVector> FindSpawnPoint(const FString& ZoneName)
|
|
431
|
+
{
|
|
432
|
+
if (ZoneName.IsEmpty()) return {}; // NullOpt
|
|
433
|
+
return FVector(100.f, 200.f, 0.f);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (TOptional<FVector> Pt = FindSpawnPoint(TEXT("Start")))
|
|
437
|
+
{
|
|
438
|
+
SpawnAt(Pt.GetValue());
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
## TVariant
|
|
445
|
+
|
|
446
|
+
`TVariant<T1, T2, ...>` is a type-safe discriminated union (like `std::variant`).
|
|
447
|
+
|
|
448
|
+
```cpp
|
|
449
|
+
#include "Misc/TVariant.h"
|
|
450
|
+
|
|
451
|
+
TVariant<int32, float, FString> Val;
|
|
452
|
+
|
|
453
|
+
// Set
|
|
454
|
+
Val.Set<int32>(42);
|
|
455
|
+
Val.Set<FString>(TEXT("Hello"));
|
|
456
|
+
|
|
457
|
+
// Check active type
|
|
458
|
+
if (Val.IsType<FString>())
|
|
459
|
+
{
|
|
460
|
+
FString& S = Val.Get<FString>();
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Visit pattern — UE5 provides Visit() via TVariantHelper
|
|
464
|
+
Visit([](auto& V) { /* handle each type */ }, Val);
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
---
|
|
468
|
+
|
|
469
|
+
## Sparse Arrays & Indirect Arrays
|
|
470
|
+
|
|
471
|
+
### TSparseArray
|
|
472
|
+
|
|
473
|
+
Used internally by TSet and TMap. Elements can have "holes" (removed indices remain allocated but invalid). Access by `FSetElementId`.
|
|
474
|
+
|
|
475
|
+
```cpp
|
|
476
|
+
// Rarely used directly — prefer TSet/TMap
|
|
477
|
+
TSparseArray<FMyStruct> SparseData;
|
|
478
|
+
FSparseArrayAllocationInfo AllocInfo = SparseData.AddUninitialized();
|
|
479
|
+
new (AllocInfo.Pointer) FMyStruct();
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### TIndirectArray
|
|
483
|
+
|
|
484
|
+
Holds heap-allocated objects; owns and deletes them on removal.
|
|
485
|
+
|
|
486
|
+
```cpp
|
|
487
|
+
TIndirectArray<FMyNonCopyable> Objects;
|
|
488
|
+
Objects.Add(new FMyNonCopyable());
|
|
489
|
+
// Objects deletes all entries on destruction
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
494
|
+
## UPROPERTY-Compatible Container Rules
|
|
495
|
+
|
|
496
|
+
When containers are UPROPERTY members:
|
|
497
|
+
|
|
498
|
+
- `TArray<UPROPERTY-type>` — fully supported, GC-tracked element pointers
|
|
499
|
+
- `TMap<UPROPERTY-type, UPROPERTY-type>` — supported
|
|
500
|
+
- `TSet<UPROPERTY-type>` — supported
|
|
501
|
+
- **Keys in TMap UPROPERTY must be value types** (no UObject* keys)
|
|
502
|
+
- `TArray<TArray<T>>` — **not supported** as UPROPERTY (use a wrapper struct)
|
|
503
|
+
- Containers of raw pointers **without UPROPERTY** are invisible to GC
|
|
504
|
+
|
|
505
|
+
```cpp
|
|
506
|
+
// Correct: TArray of TObjectPtr for UE5 GC-tracked arrays
|
|
507
|
+
UPROPERTY()
|
|
508
|
+
TArray<TObjectPtr<UMyObject>> ManagedObjects;
|
|
509
|
+
|
|
510
|
+
// Correct: TMap with value-type key and UObject ptr value
|
|
511
|
+
UPROPERTY()
|
|
512
|
+
TMap<FName, TObjectPtr<UDataAsset>> AssetRegistry;
|
|
513
|
+
|
|
514
|
+
// WRONG: nested TArray as UPROPERTY
|
|
515
|
+
UPROPERTY()
|
|
516
|
+
TArray<TArray<int32>> Matrix; // Compile error
|
|
517
|
+
|
|
518
|
+
// WORKAROUND: wrap inner array
|
|
519
|
+
USTRUCT(BlueprintType)
|
|
520
|
+
struct FIntRow { GENERATED_BODY() UPROPERTY() TArray<int32> Values; };
|
|
521
|
+
|
|
522
|
+
UPROPERTY()
|
|
523
|
+
TArray<FIntRow> Matrix;
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
---
|
|
527
|
+
|
|
528
|
+
## Performance Comparison
|
|
529
|
+
|
|
530
|
+
| Operation | TArray | TMap | TSet |
|
|
531
|
+
|-----------|--------|------|------|
|
|
532
|
+
| Add | O(1) amortized | O(1) amortized | O(1) amortized |
|
|
533
|
+
| Find/Contains | O(n) | O(1) avg | O(1) avg |
|
|
534
|
+
| Remove by value | O(n) | O(1) avg | O(1) avg |
|
|
535
|
+
| Remove by index | O(n) (shift) | — | — |
|
|
536
|
+
| Iteration | O(n), cache-friendly | O(n), sparse | O(n), sparse |
|
|
537
|
+
| Sorted iteration | Sort first: O(n log n) | No | No |
|
|
538
|
+
| Memory | Compact contiguous | Sparse + hash table | Sparse + hash table |
|
|
539
|
+
|
|
540
|
+
**Guidelines:**
|
|
541
|
+
- Use `TArray` when: ordered, index-based access, frequent iteration, small N.
|
|
542
|
+
- Use `TMap` when: key-based lookup, O(1) find matters, order irrelevant.
|
|
543
|
+
- Use `TSet` when: uniqueness enforced, fast membership test, no associated value needed.
|
|
544
|
+
- Consider `TArray` + `Sort` + binary search for read-heavy sorted lookups.
|