Your Event Graph is full of Cast To XXX nodes. And lately the game takes ages to start. Those two facts can share the same cause.
Cast To is intuitive and convenient, but overusing it piles up invisible chains. This article explains what those chains actually are and how to cut them with a Blueprint Interface. We introduced Interfaces as "a letter with no addressee" in the overview of the three communication methods. Here we dig into them from the angle of memory and load time.
What You'll Learn
- The real problem with Cast isn't speed, it's hard references (the target's baggage loads too)
- An Interface is a contract for implementation. It holds no logic and creates no dependency
- Hands-on: replace Cast-heavy damage logic with an Interface
- The right way to use
Does Implement Interface, and when to pick Cast over Interface
The Real Problem with Cast: Invisible Chains (Hard References)
Let's clear up one misconception first. The type check in Cast To is fast. It's roughly a pointer comparison, and the CPU cost is negligible.
The real problem is that the moment you write Cast To BP_Enemy, that Blueprint holds a hard reference to BP_Enemy.

A hard reference is a chain. When your Blueprint loads, the BP_Enemy at the other end of that chain loads too, and so do the meshes, textures, and sounds that BP_Enemy references. All of it gets dragged into memory together (see hard and soft reference basics). References pull in more references, and you get a dependency explosion. That's the mechanism by which overusing Cast inflates load times and memory, commonly known as the Blueprint hard reference problem.
What Is a Blueprint Interface: A Contract for Implementation
A Blueprint Interface is an asset that defines only a contract for a capability. It's the equivalent of an abstract interface in C++.

An Interface holds no data and no implementation. It only states the rule "any Actor that signs this contract can respond to a call named ApplyDamage." That gives you three benefits.
- No Cast needed: the caller can send the message without knowing the target's class
- No dependency created: a reference to the contract (the Interface) is lightweight and drags none of the target's baggage along
- Polymorphism: the same message makes the slime flash, the crate shatter, and the tower collapse. The reaction is entirely up to the implementer
Hands-On: Replacing Cast-Heavy Damage Logic
Let's migrate "deal damage to whatever the player's attack hits" from a Cast version to an Interface version. The shape is the same whether it's a melee swing in an action game or a bullet in a shooter.
First, the "before": the Cast version. It usually looks like this.

Bad example: the Cast staircase
the hit Actor → Cast To BP_Enemy → on success, TakeDamage
→ on failure → Cast To BP_Crate → on success, Break
→ on failure → Cast To BP_Tower → ... (the staircase grows with every new breakable)
Now let's replace it with an Interface.
- Create the contract: right-click in the Content Browser → Blueprint → Blueprint Interface → create
BPI_Damageable. Define the functionApplyDamage(input:DamageAmount, Float) - Sign the contract: open
BP_Enemyand addBPI_Damageablevia Class Settings → Interfaces → Add. AnEvent ApplyDamagenode appears in the Event Graph, where you implement the reaction. For a quick test, aPrint String(like "Enemy: 25 damage") is enough. For proper health management, plug in the parts from the health Component article and Component and Interface merge right there. Sign the same contract onBP_CrateandBP_Towertoo, writing each one's way of breaking (the crate shatters, the tower collapses) - Send the letter: replace the player's attack hit logic with a single node
Good example: the Interface message
the hit Actor → ApplyDamage (Message) (Damage Amount: 25.0)
The Message node (marked with an envelope icon) calls the implementation if the target has signed the contract, and safely does nothing if it hasn't. You don't even need a success/failure branch.
To finish, let's see the effect with your own eyes. Right-click your player Blueprint and open the Reference Viewer. Where the Cast version had thick reference lines running to BP_Enemy and BP_Crate, the Interface version has only a thin line to BPI_Damageable. That gap is your load time and memory difference.

The number of reference lines confirms the connections. To see the weight itself as a number, right-click the asset → Size Map, and you get the total size in MB of everything the asset drags in. Record and compare that number for the Cast version and the Interface version, and the effect of cutting the chain shows up as a real measurement.
Two things to take away.
- Adding more breakables requires no change on the attacker: a new destructible just signs the contract and joins in. No more extending the Cast staircase
- Make Reference Viewer checks a habit: the effect of decoupling is visible. Comparing "did the reference lines shrink?" before and after turns design improvement into something you can actually feel
Common Mistakes and Best Practices
| Mistake | Best Practice |
|---|---|
| Cramming too many functions into one Interface | Keep each one focused on a single purpose (split them like BPI_Damageable and BPI_Interactable) |
| Casting before an Interface call | Check implementation with the Does Implement Interface node. Casting defeats the whole purpose |
| Trying to store data in an Interface | An Interface is a contract for capability. Data belongs to each Actor |
| Using it for complex two-way exchanges | If the main goal is "notification," Event Dispatcher is often the better fit |

When you need to know in advance whether a target supports something (showing "Examine" in the UI only for interactable objects, for instance), use Does Implement Interface to check for the contract. You check the contract, not the type. That consistency is what preserves loose coupling.
Bonus: Good to Know for Later
- Message nodes are safe when they miss: send one to a target that hasn't implemented the contract and nothing happens, quietly and without an error. That tolerance is convenient, but if you feel like "I implemented it and it doesn't work," suspect a missing Interface entry in Class Settings or a missing
Event XXXimplementation - The same idea applies in C++: in C++ you create an interface class like
IDamageableand check withImplementsInterface(). It has even less overhead than Blueprint and is a standard performance optimization - Sometimes Cast is the right answer: for your own child classes or within the same functional group, where the two are already inseparable by design, Cast is fine. On the loading side, if the target is a class that stays loaded anyway (GameMode, for instance), the hard reference does no real harm
- If it's still heavy after cutting the chain: decoupling with an Interface removes needless chained loading, but if the assets you actually need are large, that calls for a different approach. Deferring the load itself with async loading and soft references is the next move
Summary: Choosing Between Cast and Interface
| Aspect | Casting | Blueprint Interface |
|---|---|---|
| Dependencies | Creates a hard reference (baggage loads too) | Only a lightweight reference to the contract |
| Coupling | High (locked to a specific class) | Low (depends only on the contract) |
| Extensibility | More Casts as targets multiply | Just have them sign the contract |
| CPU speed | Fast (pointer comparison) | Slightly slower (with virtually no real impact) |
The guideline is simple: Cast for things that are inseparable, Interface for things that may multiply. When in doubt, open the Reference Viewer and count the chains.
For the broadcast counterpart to Interfaces, head to the Event Dispatcher article. To finish off the bigger design picture after cutting down on Casts, see 10 techniques for cleaning up Blueprint graphs.
If you opened the Reference Viewer on your player Blueprint right now, how many chains would you find?