The great thing about Blueprint is that it just works, and that's also the scary part. You wire things up until they work, keep going for three months, and eventually the frame rate drops, it crashes now and then, and you can't read the graph you wrote yourself. Most people hit this wall in the same order.
The ways people trip up are fairly predictable. In this article we sort 10 common Blueprint mistakes into three groups (slow, fragile, and unreadable) and cover both "why it happens" and "what to do instead" for each. At the end, you'll open one Blueprint from your own project, give it a health check, and fix two things on the spot.
What You'll Learn
- The mistakes sort into three groups: slow (performance), fragile (stability), unreadable (maintainability)
- The big three for "slow": Tick, Get All Actors Of Class, and Pure node recalculation
- The real problem with Cast isn't speed, it's hard references dragging memory along
IsValidandUnbind Event, the two habits that prevent crashes- Hands-on: health-checking your own Blueprint and fixing two things
Why Everyone Makes the Same Mistakes
All 10 mistakes share a common background. Blueprint always puts the shortest path to "working right now" directly in front of you.
Want something to happen every frame? Wire it to Event Tick and it works. Want to touch that Actor? Grab it with Get All Actors Of Class and it works. Want to call the other object's function? Cast To it and it works. None of these are wrong. For one Actor, one call, in one place, they're the correct answer.
The problem is that those shortcuts show a different face as the numbers grow and time passes. The moment enemies go from 1 to 100, the moment levels go from 1 to 20, or the moment you open that graph three months later, the cost surfaces.
So read the 10 mistakes not as rules to memorize, but as a checklist for the question "will this survive scale and time?"
Group 1: Mistakes That Make Things Slow
First, the ones that affect performance. Frame rate drops can also come from rendering or physics, but when Blueprint is the cause, it's usually one of these three.
What all three have in common is the shape: going out to search every time you need something.

Mistake 1: Wiring Everything to Event Tick
Event Tick runs every frame. At 60 FPS that's 60 times per second per Actor. Line up 100 enemies and it's 6,000 times per second. Even if each call is light, it gets heavy the moment the multiplication kicks in.
What to do instead: Stop "always watching" and switch to "act only when something happens." Hand periodic work to Set Timer by Event, state changes to Event Dispatcher, and contact to Collision Event. What helps here isn't "switching to a Timer" as such, it's dropping the execution count from 60 per second to a few. Running the same work at the same frequency won't get lighter just because it's a Timer. If you truly need Tick, lower the frequency with Tick Interval, or wake it only when needed with Set Actor Tick Enabled.
The full rebuild is covered in Ditching Event Tick: Designing with Timers and Events, where we rebuild a guard AI from scratch.
Mistake 2: Calling Get All Actors Of Class Every Time
This node scans every Actor in the world and collects the ones matching the class. It gets heavier the bigger your level gets, and calling it inside Tick or from a frequent event is like reading the entire phone book cover to cover before making a call.
What to do instead: Get it once in Event BeginPlay and store it in a variable. If the target is the player, Get Player Character is the shortest path (though it only returns Character-derived Pawns, so use Get Player Pawn if you're using a custom Pawn). If you need multiple targets, design it so the new objects announce themselves rather than having someone search for them. Have each one register itself into a manager's array when it spawns, and the design holds up as the numbers grow.
Mistake 3: Not Noticing Pure Node Recalculation
Blueprint nodes come in two kinds: Impure nodes with execution pins (the white triangles) and Pure nodes without them. Pure nodes don't cache their values; they recalculate every time an output pin is referenced.
Connect the output of an expensive Pure node to three places, and that calculation runs three times. It looks like a single node, which makes this an easy mistake to miss.
What to do instead: For expensive Pure nodes, Set the result into a variable once and reuse it. What matters here is deciding when to Set it. If you're vague about whether you're just reusing it within that event or updating it whenever the state changes, you trade the problem for a bug where you keep using a stale value.
For the record, being Pure isn't bad in itself. Pure marks a node as "no side effects, just returns a value," and even an expensive one is fine if it's connected from only one place or only runs infrequently. What to watch out for is branching the output to multiple places.
Group 2: Mistakes That Make Things Fragile
Next, stability. These cause crashes and "sometimes it doesn't work." There are two broad causes: the other object is gone, or you know too much about the other object.

Mistake 4: Handing Out Cast To Everywhere
This surprises people, but the CPU cost of the cast itself is tiny. The real issue is the hard reference. The moment you write Cast To BP_Enemy, that Blueprint "knows about BP_Enemy," and the meshes, textures, and animations BP_Enemy owns get dragged into memory along with it.
When a UI widget casts to an enemy, and the enemy casts to a boss, and so on, opening a single UI element pulls in a huge pile of assets. That's why startup time and memory usage balloon.
What to do instead: Use a Blueprint Interface, which conveys the request without knowing the other side's type, or an Event Dispatcher, which broadcasts without knowing who's listening.
The criteria for choosing are in the Blueprint Interface article and the overview of the three communication methods.
Mistake 5: Not Checking Reference Validity
"The UI reads the enemy's HP after the enemy dies." That crashes. The Actor you were referencing has already been destroyed, and you tried to use it anyway.
What to do instead: Put an Is Valid right before you use a reference that could be null. You don't need to place one mechanically in front of every reference. Target the ones that "could be destroyed" or "could fail to be acquired," which means references to other Actors and references acquired at runtime.
One more thing worth knowing: Is Valid only stops you, it doesn't fix anything. If the player respawns, the reference you saved in BeginPlay stays invalid, so you need a separate mechanism to re-acquire it.
Mistake 6: Hammering Physics Every Frame
Calling Add Force or Set Physics Linear Velocity continuously on Tick creates a second problem on top of the load: the behavior becomes unpredictable. Your input is interrupting the physics engine's calculations every single frame.
What to do instead: Leave character movement to the Character Movement Component. Touch physics directly only for momentary hits, like an explosion knockback.
Mistake 7: Forgetting to Unbind an Event Dispatcher
If an Actor that did a Bind Event on an Event Dispatcher gets destroyed while still bound, events fire at a destroyed object. This is a classic cause of intermittent crashes.
What to do instead: If you Bind in Event BeginPlay, Unbind Event in Event EndPlay. When you write a Bind, write the Unbind right then. Make a habit of writing them in pairs and you won't forget.
For how to write Bind/Unbind, see the Event Dispatcher article.
Group 3: Mistakes That Make Things Unreadable
Finally, maintainability. This is the group that comes back to bite you three months later.

Mistake 8: Cramming Everything Into One Graph
Once dozens of nodes hang off a single event and the wires start crossing, that graph is no longer something you read. It's something you decipher.
What to do instead: Extract each cohesive chunk of logic into a Function. Collapse Graph, which just wraps nodes, does tidy things up, but you can't reuse or test it, so aim for a Function first.
There is one constraint: you can't place Latent nodes (nodes that take time to complete) like Delay or AI Move To inside a Function. When you want to extract that kind of logic, use a Macro or a Collapsed Graph.
How to decide the extraction unit is in 10 Techniques for Organizing Blueprint Graphs, and choosing between the two is in Function vs Macro.
Mistake 9: Putting Every Variable in the Variables Panel
Making everything a Class Variable, right down to loop totals and temporary calculation results, doesn't just bloat the variable list. It causes real damage: the previous value sticks around and the second run behaves incorrectly.
What to do instead: Put values used only within a function into Local Variables. They start from their default value on every call, so the whole concept of "forgot to reset" disappears.
A walkthrough that reproduces and fixes the "the total doubles on the second run" bug is in Class variables vs local variables.
Mistake 10: No Comments, No Naming
Blueprint is visual, so it's easy to assume the picture speaks for itself. But open it a few weeks later and the picture won't tell you why that branch is there.
What to do instead: Wrap each chunk of logic in a Comment Box and write "why it's done this way," not "what it does." Even just aligning naming, like starting Booleans with b (bIsDead) and Interfaces with BPI_, changes how readable the lists are.
Hands-On: Giving Your Own Blueprint a Health Check
Reading this far fixes nothing. Open one Blueprint from the project you're building right now and diagnose it on the spot. A metroidvania enemy, a tower defense turret, a horror game thing that stalks you: the roles differ, but the symptoms are usually the same.
Here we'll use the enemy Blueprint in the shape most people write first.
Setup to reproduce this: Assume BP_Enemy has these variables.
| Variable | Type | Default | Role |
|---|---|---|---|
ChaseRange | Float | 800.0 | Distance at which chasing starts |
PlayerRef | Actor | None | Reference to the player (not held yet) |
There's a reason the type is Actor and not BP_Player. The moment you specify BP_Player as a variable type, you create the hard reference described in mistake 4. Here we only pass it as the destination for AI Move To, so Actor is enough.
On top of that, prepare the following on the enemy.
- Add a DetectionSphere (Sphere Collision Component, Radius 800) and check
Generate Overlap Eventsin the Details panel. Set the Collision Preset toCustomand set Overlap on the Pawn channel only, with everything elseIgnore(usingOverlapAllDynamicmakes it react to projectiles and props too) - Prerequisites for AI Move To: the enemy must derive from Pawn/Character, an AI Controller Class must be set and actually possessing it, and a Nav Mesh Bounds Volume covering the movement area must be placed in the level (see NavMesh setup)
The pre-diagnosis graph looks like this. Can you spot the three symptoms happening at once?

Event Tick
→ Get All Actors Of Class (Actor Class: BP_Player) ← mistake 2: full scan every frame
→ Get (Index 0)
→ Cast To BP_Player ← mistake 4: hard reference
→ Get Distance To (Target: Cast result)
→ Branch (Distance < ChaseRange)
True → AI Move To (Goal Actor: Cast result)
Diagnosis: a full Actor scan every frame (mistake 2), a Cast every frame (mistake 4), and standing watch every frame at all (mistake 1). There's one more thing you shouldn't miss: it keeps firing AI Move To for as long as the condition holds. A move request only needs to be issued once and the Pawn will run to the destination, so there's no need to re-issue it every frame.
The fix takes three moves.
- Stop searching: Call
Get Player Characteronce inEvent BeginPlayand store it inPlayerRef. From then on you can use that variable anywhere - Stop standing watch: Throw away the distance check on
Event Tickand replace it withOn Component Begin OverlaponDetectionSphere. When the player gets close, they'll let you know - Confirm who it is before moving: Overlap can fire for things other than the player, so compare whether the Actor that came in is the same as
PlayerRef. Then run it throughIs Valid
Here's the graph after the fix.

Event BeginPlay
→ Get Player Character
→ Set PlayerRef ← searching happens once, at startup
→ Set Actor Tick Enabled (false)
On Component Begin Overlap (DetectionSphere)
→ Is Valid (PlayerRef)
Valid → Branch (Condition: Other Actor == PlayerRef) ← is the thing that entered really the player?
True → AI Move To (Pawn: Self, Goal Actor: PlayerRef)
Hit Play and stand somewhere away from the enemy. The enemy shouldn't move at all until you enter the detection range, and should start chasing the instant you do. If you see that, it worked.
If it doesn't behave as expected, drop one Print String on On Component Begin Overlap to isolate the problem (see checking values with Print String).
- No log appears → the Overlap isn't firing. Check
Generate Overlap Eventsand the collision settings onDetectionSphere - The log appears but nothing moves → it's the AI side. Check that an AI Controller is assigned, that
PawnonAI Move Tois wired to Self, and that the level has a Nav Mesh Bounds Volume - It chases from the very start → the radius is too large, or it's reacting to things other than Pawns
Two things to take away.
- Stop searching and several mistakes disappear at once: Just dropping "search every frame" resolved mistakes 1, 2, and 4 together. Hold your references in advance, and let the trigger come to you. Those two habits fix most of group 1
- Visual confirmation and load measurement are different things: "The enemy is standing still" only tells you the design changed. How much lighter it got is what
stat unitmeasures (see measuring with stat commands). To really feel the effect,Alt-drag to duplicate the enemy up to 30 copies, then compare thestat gamenumbers before and after the fix. With one enemy the difference is noise, but as the count grows only the Tick version's number keeps climbing
If you want to remove the Cast entirely, head to the Blueprint Interface article. If you want to trace the values themselves, head to stopping execution with breakpoints.
Bonus: Good to Know for Later
"C++ is faster than Blueprint" is, strictly speaking, only half true. Blueprint nodes are interpreted and executed one at a time on a virtual machine, so stepping from node to node carries overhead in itself. That said, the insides of built-in nodes like AI Move To are written in C++ and run at full speed. In other words, Blueprint-specific slowness shows up in loops that step through large numbers of small nodes. Even so, some nodes like AI Move To or enumerating Actors are expensive in what they do internally, so "it's a built-in node, therefore it's safe" isn't a rule either. When you want to write calculations that run tens of thousands of times, or complex math every frame, that's the moment to consider C++. Conversely, in most cases fixing the 10 items in this article does far more than moving to C++.
Have a way to find the mistakes, too. "It feels heavy" will send you to fix the wrong place. Press ` (backquote) in the editor to open the console and type stat unit to get a breakdown of Frame / Game / Draw / GPU. A large Game number is a sign the game thread is congested. But Game also includes AI, animation, physics, and UI, so narrow it down further with stat game and friends. If Blueprint looks likely, start with group 1 of this article.
Don't try to fix everything at once. Out of the 10 items, only one or two matter right now. Get a read with stat unit first, then fix only that group. Not breaking things that work is a fine best practice in its own right.
Summary
The 10 mistakes are more useful when you can pull them up by which group's symptoms you're seeing than by memorizing them in order.
| Group | Symptoms | Main mistakes | Motto |
|---|---|---|---|
| Slow | FPS drops, stutter | Tick overuse / Get All Actors Of Class / Pure recalculation | Don't go searching, hold it in advance |
| Fragile | Crashes, sometimes doesn't work | Cast abuse / missing IsValid / physics hammering / forgotten Unbind | Check before use, unbind what you bind |
| Unreadable | Scared to modify | Giant graphs / where variables live / no comments | Explain it to yourself three months from now |
The common thread is that a way of writing that's correct for one Actor, one call, in one place won't necessarily survive scale and time.
That Blueprint you have open right now: could it fight in the same shape with 100 enemies on screen?