Quick answer: Pooled actors keep their tick state between reuses, so an actor that was disabled when returned to the pool stays disabled when retrieved. BeginPlay only fires once per lifetime. Add an Activate method that re-enables actor tick, iterates components to re-enable their ticks, and resets any state BeginPlay used to set.
Here is how to fix Unreal actors with Tick disabled after respawn from a pool. You built an object pool to avoid spawning hitches: bullets, pickups, enemy projectiles, anything that gets created and destroyed often. The first wave plays perfectly. The second wave looks identical to the first — until the AI freezes mid-air, the projectiles drift in a straight line without rotation, and your timeline does not advance. The actors are visible. They have collision. They are simply not ticking. The pool returned a corpse.
The Symptom
An actor retrieved from an object pool appears in the world but its per-frame logic does not execute. Different gameplay systems break in different ways depending on what each actor relies on Tick for.
AI does not move. Enemies stand still in the location they were placed. Behaviour Tree updates do not run because the AIController’s tick is disabled. The Pawn renders with idle animation but never receives a Move To order, never rotates toward the player, never even starts a perception scan.
Timelines and tweens do not advance. A pickup that was animating a hover bob via Timeline now sits perfectly still. Health pulses, glow flicker, scaling animations — anything driven by a Timeline component that is set to TickGroup PrePhysics or DuringPhysics — freezes at the value it had when the actor entered the pool.
Projectile movement freezes. Projectile actors travel in a straight line at constant velocity (because the physics step is independent of actor tick) but their UProjectileMovementComponent stops applying gravity, homing, or bounce because the component tick is off. The projectile flies through walls or ignores its target.
BeginPlay-set state never reapplies. Anything you initialised in BeginPlay — binding to delegates, registering with managers, kicking off particle systems — only happened the first time. Subsequent retrievals get an actor whose internal state matches the previous use, including any flags set during destruction.
What Causes This
SetActorTickEnabled persists across pool reuse. When you returned the actor to the pool, your cleanup code likely called SetActorTickEnabled(false) to stop logic from running on the dormant actor. That state lives on the actor instance, not on a per-spawn basis. When the pool hands the actor back to gameplay code, tick is still disabled because nothing re-enabled it.
BeginPlay only fires once. The lifecycle hook you would normally use to set up tick state runs exactly once per actor lifetime, on first spawn. Pooled actors are reused, not respawned, so BeginPlay never fires again. Any setup written to BeginPlay silently does nothing on the second use.
Component ticks are independent of actor tick. SetActorTickEnabled toggles only the actor’s own PrimaryActorTick. Every USceneComponent and UActorComponent with a PrimaryComponentTick retains its own enabled state. Disabling the actor does not propagate to components, and re-enabling the actor does not propagate either.
Level streaming detached the actor. If the actor was placed in a streamed sub-level that unloaded while it was pooled, Unreal may have set its tick to disabled as part of the stream-out routine. When you retrieve the actor, the parent level may still be streaming back in, and the streaming subsystem can override your re-enable call before the next frame.
The Fix
Step 1: Replace BeginPlay-only setup with an Activate method. Define a virtual Activate() method on your pooled actor base class. Call it from the pool whenever an actor is taken out. Mirror it with a Deactivate() method that the pool calls when the actor is returned. Move every “reset to fresh state” line out of BeginPlay and into Activate.
// Base class for any pooled actor
void APooledActor::Activate(FVector NewLocation,
FRotator NewRotation)
{
SetActorLocationAndRotation(NewLocation, NewRotation);
SetActorHiddenInGame(false);
SetActorEnableCollision(true);
// Re-enable tick on the actor itself
SetActorTickEnabled(true);
// Re-enable tick on every component that wants it
for (UActorComponent* Comp : GetComponents())
{
if (Comp && Comp->PrimaryComponentTick.bStartWithTickEnabled)
{
Comp->SetComponentTickEnabled(true);
}
}
bIsActive = true;
OnActivated(); // Hook for subclasses
}
void APooledActor::Deactivate()
{
SetActorHiddenInGame(true);
SetActorEnableCollision(false);
SetActorTickEnabled(false);
for (UActorComponent* Comp : GetComponents())
{
if (Comp)
{
Comp->SetComponentTickEnabled(false);
}
}
bIsActive = false;
}
The key detail is the bStartWithTickEnabled check. Components that were never meant to tick (a static mesh, a billboard) should not be force-enabled by Activate. Honour the original component configuration to avoid waking up dormant systems.
Step 2: Validate prerequisites before activating. If the actor uses AddTickPrerequisiteActor to ensure a manager ticks first, confirm the prerequisite is alive and ticking. If the prerequisite was destroyed while the actor was pooled, the dependency is dangling and tick may not register correctly.
void APooledProjectile::OnActivated()
{
if (AActor* TickRoot = ProjectileManager.Get())
{
AddTickPrerequisiteActor(TickRoot);
}
else
{
UE_LOG(LogPool, Warning,
TEXT("Projectile activated without manager"));
}
// Defensive: re-bind delegates that BeginPlay would have set
UProjectileMovementComponent* Move =
FindComponentByClass<UProjectileMovementComponent>();
if (Move)
{
Move->Velocity = InitialVelocity;
Move->SetUpdatedComponent(GetRootComponent());
Move->SetComponentTickEnabled(true);
}
}
The SetUpdatedComponent call is critical for projectile movement specifically — if the root component reference was cleared during pool return, the movement component will silently no-op every tick.
Level Streaming Considerations
If your pool contains actors that were placed in the level (rather than spawned at runtime), level streaming events can interfere with tick state. When a streaming level unloads, all its actors are detached and their ticks disabled. When the level streams back in, ticks are re-enabled by the streaming subsystem. If your Activate call happens between unload and re-load, your re-enable can be overwritten by a subsequent stream event.
The safest pattern is to spawn pool actors as transient (not part of any saved level) and parent them to a persistent manager actor. The pool then has full control over tick state and is not subject to streaming-induced overrides. If pool actors must live in a streamed level, register a OnLevelLoaded callback on the streaming volume and re-validate tick state for any active pool members after the stream completes.
“BeginPlay is for first contact. Activate is for every contact after that. If you only have BeginPlay, you only have one life.”
Related Issues
If your pool grows unbounded because actors are not being returned, see Unreal Actor Pool Leak for return-on-destroy and lifetime tracing patterns. If your actors tick but the timeline still does not advance, check Timeline Paused After Pool Reuse for Timeline-specific reset steps.
Tick state is persistent — treat it the way you treat health: explicitly reset it on every spawn.