Hard vs soft references
A hard reference loads the other asset with yours, and everything it hard-references, all the way down. A
soft reference is just a path; nothing loads until you ask. A Cast To BP_Something node is a hard
reference. That's how a small prop ends up dragging 400 MB into memory.
Hard vs soft
| Hard | Soft (TSoftObjectPtr, TSoftClassPtr, FSoftObjectPath) | |
|---|---|---|
| Stores | A pointer, recorded in the package's import table | A path string |
| Loading mine loads it? | Yes, synchronously and transitively | No. Load on request (async via FStreamableManager / Asset Manager) |
| Cost | Memory and load time | Latency. Code must handle "not loaded yet" |
| Use for | Small things always needed with the owner | Big or optional things: meshes, textures, sounds, levels, UI, runtime picks |
How one reference becomes 400 MB
- The chain is transitive: mesh → material → textures. Worse: Blueprint → Blueprint. If a prop BP references or casts to a manager BP, it pulls in the manager and everything the manager references.
- Memorise: a
Cast To <BP class>node creates a hard reference to that class. So do variables typed as a BP class, and hard class defaults. It looks like nothing in the graph. - Why the editor hides it: the editor loads on demand with no memory budget. Everything is "already loaded", so the chain costs nothing. It shows up at cook/runtime as hitches, memory spikes and bloated paks.
Finding it
| Tool | Shows |
|---|---|
| Reference Viewer (right-click asset) | What this pulls in, and what pulls it in |
| Size Map | Total size including dependencies: "this prop is 400 MB" |
| Asset Audit | Every asset sorted by total size |
memreport -full, obj list | What's actually resident in a running build |
| A script (the tools answer) | Walk every BP via the asset registry, sum dependencies, flag anything over a threshold. Run it in CI. |
Fixing it
- Soft references + async load for big or optional assets.
- Replace casts to heavy BPs with interfaces or event dispatchers. The caller no longer depends on the class.
(WadOfGore's
IAbilitySystemInterfaceis this idea in C++.) - Shared data in data assets instead of manager BPs. Constants in function libraries or C++.
- C++ base classes: casting to a C++ class doesn't pull in any Blueprint's assets. That's one reason the course puts the character logic in C++ and only assets in the BP child.
Open Size Map on the heaviest Blueprint in Neon Echo (or BP_HeroCharacter in WadOfGore). Write down the
number and what's in the chain. That becomes an interview answer with a real number in it.
Quiz yourself
What happens when you load an asset with a hard reference to another?
The referenced asset loads too, synchronously, and so does everything it hard-references, transitively.
Which innocent-looking Blueprint node creates a hard reference?
Cast To <Blueprint class>. (Also variables typed as a BP class.)
Why doesn't reference bloat show up while working in the editor?
The editor has usually loaded everything already, with no memory budget or cook step, so the chain looks free.
Reference Viewer vs Size Map?
Reference Viewer: the graph of what references what. Size Map: total memory/disk including dependencies.
Two ways to talk to a manager BP without casting to it.
A Blueprint Interface, or an event dispatcher. (Or put the logic in a C++ base class / subsystem.)
Cost of switching to soft references?
Latency and complexity: you load on demand (ideally async) and must handle the not-yet-loaded / null case.