CrimsonObjectPoolv1.0Coming to FabFree
Join the Discord

Recycle actors instead of spawning and destroying them, with an explicit acquire/release API and a documented path for replicated actors.

Free · Performance · Unreal Engine 5

Spawn it once. Reuse it all match.

Projectiles, impact effects, pickups and shell casings pay for a full SpawnActor and Destroy every single time. CrimsonObjectPool replaces that with a per-world subsystem that recycles actors by class: Acquire Actor hands you a live instance, Release Actor parks it for the next acquire. Replicated actors included.

Why CrimsonObjectPool

Pooling that stays predictable: explicit at every call site, correct over the network, and removable in one command.

Explicit, never implicit

The plugin never hooks engine spawn paths. An actor is pooled only where you asked for it to be, so there is no hidden behaviour to reason about when something goes wrong, and nothing changes for the rest of your project.

Replicated actors, done right

Parked actors go dormant rather than being hidden at a far-off location. A hidden, non-colliding or net-culled actor loses relevancy, and every client destroys its copy, which is the cost the pool exists to remove. Acquire and release are server-only for a replicating class.

Bulk spawns without the hitch

Request Actor Queued serves acquires across frames under a MaxSpawnsPerFrame budget (2 by default) and hands each one back through On Pooled Actor Ready. Requests a warm pool can fill from its free list skip the budget entirely; only real spawns are throttled.

Sizing you control per class

Default Capacity, growth policy (Grow or HardCap) and over-capacity behaviour are project-wide defaults; a Class Overrides entry can change any of them plus a prewarm count for one class. Override lookup walks up the class hierarchy, so one entry covers a whole family of projectiles.

Counters that prove it works

Crimson.ObjectPool.Dump prints pooled, live, high-water, created and spawns-avoided per class. If created keeps climbing, instances aren't being released, and you can see it in one command. A pool-pressure ratio also warns once per class when the live count reaches 90% of capacity.

Rule pooling out instantly

Crimson.ObjectPool.Bypass 1 turns every acquire into a real SpawnActor and every release into a real Destroy. Reproduce a bug with pooling off and you know immediately whether the pool is involved.

A closer look

Two node swaps and you're pooling

Replace Spawn Actor from Class with Acquire Actor, and Destroy Actor with Release Actor. That's the whole integration for a cosmetic actor: no interface, no subclass, no settings changes.

  • You cannot tell whether an acquire recycled or spawned, and you don't need to
  • Acquire Actor Deferred hands over a dormant instance to configure; Finish Acquiring Actor brings it in

The reuse contract

A pooled actor is never destroyed, so BeginPlay runs once per lifetime, not once per use. One-way latches stay latched and per-use delegate bindings multiply. The optional ICrimsonPoolable hooks let you control reactivation yourself.

  • ICrimsonPoolable ships here; for a class you cannot edit, a C++ adapter supplies the same hooks
  • Write On Released To Pool to stand the actor down even when released mid-use
  • A delayed release, a pool drain and a manual release can all reach an unfinished actor

Who decides when a use ends

Two approaches, and they are not interchangeable. Release Actor After(Actor, Seconds) is a blunt cutoff owned by the caller, right for impacts, decals and casings, which have no notion of having finished. An actor with real completion semantics should own the decision instead.

  • Give it per-use launch parameters and let it close its own state
  • In C++, publish a release delegate the pool binds once, so it reclaims itself
  • Stack both and one actor gets two independent cutoffs; whichever lands first wins

The trap that reaches production

COND_InitialOnly sends a property only in an actor's first bunch. A recycled actor's channel is already open on its second use, so the property is never resent. Nothing errors and nothing logs.

  • Clients keep acting on the first use's data
  • The docs call it out before you write the code

Technical details

Engine
UE 5.8
Tested platforms
Windows
Blueprint-ready
Yes
C++ required
No
Network replicated
Yes
Dependencies
DeveloperSettings, NetCore

Frequently asked questions

Do I need C++ to use it?
No. Acquire, release, deferred and queued acquires, prewarm, drain and every stat are Blueprint-accessible. Two optional extras are C++-only: GetPoolReleaseDelegate, a way for an actor to reclaim itself, because a Blueprint cannot hand out a delegate reference (Blueprints call Release Actor instead), and ICrimsonPoolableAdapter, for pooling a class you cannot edit.
Does it work in multiplayer?
Yes, and it's fully supported, but pooling a replicated actor has rules, because replication is built around actors being created and destroyed and a pooled actor is neither. Acquire and release are server-only for replicating classes, net dormancy stays on while parked, and a monotonic counter in your activation state keeps two consecutive uses from looking byte-identical. Read How-To: Pool a Replicated Actor before you write the code.
Do I have to implement an interface?
No, it's optional. Without it the pool handles reactivation for you. Implementing ICrimsonPoolable makes your OnAcquiredFromPool the only reactivation that happens, which is what you want when an actor needs to reset its own state. There is no middle setting: two owners writing the same visibility and collision state is the failure mode the exclusivity rule exists to prevent.
Is there anything that can't be pooled?
One kind of actor: anything with bOnlyRelevantToOwner. Changing the owner on reuse makes it non-relevant to the previous owner, whose client destroys it; the pool warns once per class if you try. There is also one ordering rule worth knowing: prewarming is refused before the world has begun play, because actors spawned that early run BeginPlay while parked in the free list and undo their own deactivation. From BeginPlay onward is always safe.
Does it depend on other Crimson plugins?
No. It is fully standalone, needs no engine plugins, and knows nothing about projectiles or any other system. ICrimsonPoolable ships in this plugin, and a class you cannot edit is pooled through a registered ICrimsonPoolableAdapter. With CrimsonCore, CrimsonHitDetection's ACrimsonProjectile is registered automatically and pooled launch ability tasks are added; standalone, write that glue yourself.
Is it really free?
Yes. It's part of the free foundation tier, alongside CrimsonCommon and the other utility plugins.

Stop paying for spawns you already paid for

Swap two nodes, watch spawnsAvoided climb, and keep the bypass switch in your pocket for the day something looks wrong. The Quick Start pools a cosmetic actor end to end in about five minutes.