💥 Lesson 5.1: Collision, Overlaps & Hit Events
Interaction starts with collision: a pickup detecting the player, a bullet striking a wall, a trigger firing when someone enters. Unreal's collision system is powerful but full of settings that must line up — get them right and events flow; get them wrong and nothing happens, silently.
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Distinguish overlap from blocking hit and know which fires which event
- Explain collision channels, object types, and responses
- Configure a component's collision in C++
- Bind
OnComponentBeginOverlap,OnComponentEndOverlap, andOnComponentHit - Write a working pickup that grants on overlap and destroys itself
Estimated Time: 60 minutes
Engine Version: Unreal Engine 5.8
In This Lesson
Overlap vs Blocking Hit
Two fundamentally different interactions, two different event families:
| Overlap | Blocking Hit | |
|---|---|---|
| Physical effect | Objects pass through each other | Objects stop each other |
| Fires | OnComponentBeginOverlap / EndOverlap | OnComponentHit |
| Typical use | Pickups, triggers, detection zones | Walls, projectiles that impact, physics |
(pass through)"] Q -->|"Either set to Block"| B["Blocking hit — they stop
Hit event fires"] Q -->|"Ignore"| N["Nothing — no events"]
📖 The rule that trips everyone up
For an overlap event to fire, both components must be set to overlap each other, AND the moving one must have Generate Overlap Events enabled. Miss any of those and you get silence — no error, just nothing. Half of all "my collision isn't working" issues are a missing overlap-generation flag.
Channels & Responses
Collision is organized by channels. Every collidable component has an object type (which channel it is) and a set of responses (how it reacts to each channel: Ignore, Overlap, or Block).
(e.g. Pawn, WorldStatic)"] Comp --> R["Responses to each channel:
Ignore / Overlap / Block"]
- Object type — the channel this component belongs to:
Pawn,WorldStatic,WorldDynamic,PhysicsBody, or a custom channel you define in Project Settings. - Response — for each channel, one of
ECR_Ignore,ECR_Overlap,ECR_Block. - Collision presets — named bundles of these settings (
"Pawn","OverlapAllDynamic","BlockAll","Trigger") so you rarely set every response by hand.
✅ Presets first, custom later
Start with a preset that matches your intent — "OverlapAllDynamic" for a pickup, "BlockAll" for a wall. Only reach for per-channel responses when a preset doesn't fit. Custom channels (Project Settings → Collision) are for gameplay-specific categories like an "Interaction" trace channel — we use one in Lesson 5.2.
Configuring Collision in C++
You set collision on the component, usually in the constructor. A trigger sphere for a pickup wants to overlap pawns and block nothing:
#include "Components/SphereComponent.h"
APickup::APickup()
{
Collision = CreateDefaultSubobject<USphereComponent>(TEXT("Collision"));
RootComponent = Collision;
Collision->SetSphereRadius(80.0f);
// Use the trigger-style preset: query-only, overlaps dynamic things.
Collision->SetCollisionProfileName(TEXT("OverlapAllDynamic"));
// Or configure explicitly:
Collision->SetCollisionEnabled(ECollisionEnabled::QueryOnly); // no physics, just events
Collision->SetCollisionObjectType(ECC_WorldDynamic);
Collision->SetCollisionResponseToAllChannels(ECR_Overlap);
Collision->SetGenerateOverlapEvents(true); // ← the flag people forget
}
ECollisionEnabled | Meaning |
|---|---|
NoCollision | Off entirely |
QueryOnly | Overlaps & traces, but no physics (triggers, pickups) |
PhysicsOnly | Physics simulation, no queries |
QueryAndPhysics | Both (solid physical objects) |
⚠️ QueryOnly for triggers
A pickup or trigger volume should be QueryOnly — it detects overlaps but doesn't shove the player around with physics. Using QueryAndPhysics on a trigger can knock actors off course. Match the collision-enabled mode to whether the thing is physical or just a sensor.
Binding Collision Events
Collision events are dynamic multicast delegates (the full mechanism is Lesson 5.4). You subscribe your function with AddDynamic, and your function's signature must match the delegate exactly. Bind in BeginPlay (the component exists and the world is live).
// Header — the handler signatures are fixed by the delegate types:
UFUNCTION()
void OnBeginOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION()
void OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
void APickup::BeginPlay()
{
Super::BeginPlay();
// Subscribe our UFUNCTIONs to the component's delegates.
Collision->OnComponentBeginOverlap.AddDynamic(this, &APickup::OnBeginOverlap);
}
void APickup::OnBeginOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
bool bFromSweep, const FHitResult& SweepResult)
{
// OtherActor is who entered our sphere.
if (IsValid(OtherActor) && OtherActor != this)
{
UE_LOG(LogTemp, Log, TEXT("%s entered pickup"), *OtherActor->GetName());
}
}
⚠️ Handlers bound with AddDynamic MUST be UFUNCTION()
AddDynamic binds by name through the reflection system, so the target function must be marked UFUNCTION() and its signature must match the delegate's parameters exactly. A missing UFUNCTION() or a mismatched signature fails to bind (often with a cryptic error). Copy the signature from the engine's delegate declaration to be safe.
A Complete Pickup
Combining collision with the UHealthComponent pattern from Lesson 3.3 and Destroy() from 3.4 — a health pickup that heals whoever has a health component and removes itself:
void AHealthPickup::OnBeginOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
bool bFromSweep, const FHitResult& SweepResult)
{
if (!IsValid(OtherActor) || OtherActor == this) { return; }
// Composition in action (Lesson 3.5): does the overlapper have health?
if (UHealthComponent* Health = OtherActor->FindComponentByClass<UHealthComponent>())
{
Health->Heal(HealAmount); // grant the effect
UE_LOG(LogTemp, Log, TEXT("Healed %s by %.0f"), *OtherActor->GetName(), HealAmount);
Destroy(); // consume the pickup (deferred GC — Lesson 2.5)
}
// No health component? It's not a valid target; leave the pickup for someone who is.
}
✅ Look how the modules stack
This one handler uses collision (5.1), FindComponentByClass capability-checking (3.5), the health component (3.3), IsValid guarding (2.5), and Destroy (3.4). That layering is the point of the course — you're now composing patterns, not learning isolated tricks. This exact pickup returns, generalized, in the Module 6 hybrid pattern and the Module 12 capstone.
Hands-on Exercise & Quiz
🏋️ Exercise: A damage volume
Objective: Build a lava/hazard trigger.
- Create
ADamageVolume : public AActorwith aUBoxComponentroot,QueryOnly, overlapping dynamic objects, overlap events on. - Bind both
OnComponentBeginOverlapandOnComponentEndOverlap. - On begin overlap, if the actor has a
UHealthComponent, start harming it; on end overlap, stop. (You'll wire the repeating tick with a timer in Lesson 5.3 — for now just log begin/end.)
💡 Hint — end overlap signature
UFUNCTION()
void OnEndOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
// Bind: Box->OnComponentEndOverlap.AddDynamic(this, &ADamageVolume::OnEndOverlap);
🎯 Quick Quiz
Question 1: Which event fires when two components pass through each other?
Question 2: Your overlap event never fires. Most likely missing flag?
Question 3: A function bound via AddDynamic must be:
Summary
🎉 Key Takeaways
- Overlap (pass-through) fires
OnComponentBeginOverlap/EndOverlap; a blocking hit firesOnComponentHit. - Collision = an object type (which channel you are) + responses (Ignore/Overlap/Block per channel); use presets first.
- Configure with
SetCollisionProfileNameor explicitSetCollisionEnabled/ObjectType/Response; triggers areQueryOnly; don't forgetSetGenerateOverlapEvents(true). - Bind events in
BeginPlaywithAddDynamic; handlers must beUFUNCTION()with exact signatures. - A real pickup layers collision +
FindComponentByClass+ a component effect +Destroy()— patterns composing.
📚 Additional Resources
🚀 What's Next?
Overlaps detect things that touch. But often you need to ask "what's over there?" without waiting for contact — shooting a ray, checking line of sight, finding what's under the cursor. That's traces, next.
🎉 Lesson complete!
The world can feel contact now. Next, let it sense at a distance.