CrimsonCombatTextv1.0Coming to FabPersonal $9.99 · Professional $19.99
Join the Discord

Floating combat text done right: styled by tags, moved by curves, placed where you want it, pooled, and replicated for multiplayer.

Gameplay · Combat Text · Unreal Engine 5

Damage numbers that pop, without the frame drops.

Floating combat text for damage, healing and custom values. The server broadcasts once; each client runs its own visibility chain, resolves a style from a hierarchical tag registry, plays a curve-authored motion preset and recycles a pooled widget. Combat reads clearly and never hitches under a flurry of hits.

Why CrimsonCombatText

Everything you need to show numbers above actors: pooled for performance, styled by tags, placed by policy, and correct in multiplayer.

Hierarchical tag-to-style registry

A UCrimsonCombatTextRegistry maps gameplay tags to styles hierarchically: a hit tagged Damage.Fire.Burn falls back to Damage.Fire, then Damage, then DefaultStyle. Author only the specificity you care about; one Damage row already covers every damage type. AdvancedRules tag queries are the escape hatch.

Modifiers instead of combinations

Conditions that cut across types (Crit, Blocked, Absorbed) are Modifiers that layer onto whichever style matched. One can wrap the text, retint it, punch its scale or swap the motion preset, so a registry grows as styles plus conditions, not styles times conditions.

Placement decides where text lands

Where a pop appears is a UCrimsonCombatTextPlacement row a style points at: over the target, below it, a fixed HUD area, or pinned to a target frame or per-enemy nameplate. Each carries a Fallback chain: a hidden health bar's pop reverts to the actor.

Pooled views, capped and recycled

The subsystem simulates every live instance in one loop, then hands the whole frame to a renderer. Views are pooled and recycled, never destroyed mid-session; MaxActiveTexts caps concurrent pops and retires the oldest first; creation is throttled per frame when the pool runs dry.

Multiplayer by design

A UCrimsonCombatTextReplicationComponent on your GameState sends each event over unreliable RPCs: one multicast, or per-client delivery when ServerCullDistance is set. BroadcastCombatText validates authority itself, so it is safe to call from shared code like an AttributeSet's PostGameplayEffectExecute.

Curve-authored motion presets

Motion lives in a shared UCrimsonCombatTextMotionSet: ten editable built-in presets (FloatUp, ArcOut, CritPunch, Shiver and more) plus your own. Curves run over a normalized 0 to 1 lifetime, so motion completes exactly at Duration, and changing the duration restretches the shape without redrawing a curve.

A closer look

One server event, every client renders locally

On the server, build a FCrimsonCombatTextEvent and call BroadcastCombatText from an AttributeSet's PostGameplayEffectExecute, an ability, or Blueprint. Each client's subsystem filters, styles, places and displays from there.

  • Validates authority itself, so the call is safe from shared code
  • Spawn location comes from the target's UCrimsonCombatTextTargetComponent: a socket, an offset, or 100 cm above the root
  • Never created on a dedicated server or in an editor preview world

Styled by tags, moved by curves

Add a new look by adding a row: no branching code, no per-combination explosion. Editing one preset restyles every effect that references it.

  • Fixed resolution order: advanced rules, hierarchical tag match, default style, modifiers in ApplyOrder, motion preset, placement
  • Advanced rules run first only if you authored any

Bursts that read instead of blur

A deterministic spread fan deflects concurrent hits to alternating sides: slot 0 straight up, then out to FanWidth, with optional jitter if perfect regularity looks mechanical.

  • AggregationWindow folds repeat hits on the same target and style into one climbing number
  • Scopes accumulate everything landed during a stagger or posture break into one summary pop when it ends
  • Scopes open themselves from a GAS tag, or you call begin and end yourself without GAS

Every player sees their own fight

Visibility is an ordered chain of rules in a UCrimsonCombatTextFilterSet data asset, not a fixed list of toggles: rows are evaluated top to bottom and the first one with an opinion wins, so the order is the policy.

  • Seven built-in rows ship: distance cap, healing, incoming, crits-only, own, ally, enemy
  • A rule of your own is one Matches override in Blueprint
  • With CrimsonCore, every row becomes an options-screen setting automatically; standalone, the per-player overrides are yours to wire
  • IsActorFriendly is the single hook that makes Self / Ally / Enemy mean something in your project

The dev tools tell you why a number did not appear

An editor-only window (Tools → Crimson Combat Text, or Crimson.CombatText.DevTools in PIE) fires test broadcasts at any pawn from any instigator, bursts 200 at once to stress the pool cap, and live-edits the local filters.

  • Resolver tab shows which registry row won, which modifiers applied and which placement resolved
  • Filters tab prints a decision trace naming the row that hid an event
  • Status tab reports loaded assets, the GameState component and live active/pooled counts
  • Picks which PIE world it inspects, so you can compare what each client sees
The Crimson Combat Text Dev Tools Filters tab: the ordered filter chain with Show Incoming, Critical Hits Only, Show My Effects, Show Ally Effects and Show Enemy Effects, each with its match rule, above the Decision trace panel and its Test current Spawn tab event button

Technical details

Engine
UE 5.8
Tested platforms
Windows
Blueprint-ready
Yes
C++ required
No
Network replicated
Yes
Dependencies
GameplayAbilities, Niagara, SlateIM, GameplayTags, UMG, DeveloperSettings

Frequently asked questions

Do I need C++ to use it?
No. The registry, motion set, placement set, filter set, widget Blueprint, project settings, target component and per-player settings are all authored in the editor, and BroadcastCombatText is BlueprintCallable. The two extension points are BlueprintNativeEvents as well: a custom visibility rule is one Matches override on a Blueprint child of UCrimsonCombatTextFilter, and a custom placement is AcquirePlacement / UpdatePlacement on a Blueprint child of UCrimsonCombatTextPlacement, with the projection maths exposed as Blueprint nodes. The C++ snippets in the docs show the common GAS integration point, but they are optional.
Does it depend on other Crimson plugins?
No. It is fully standalone and runs on engine plugins only (GameplayAbilities, Niagara, SlateIM, UMG, GameplayTags). Team relationship comes from whatever you bind to IsActorFriendly, so it needs no team plugin. With CrimsonCore, filter rows become options-screen settings and show your project's tag labels automatically; standalone, rows show each filter's DisplayName, or bind TagLabelResolver yourself.
Does it work in multiplayer?
Yes, that is the design. A replication component on your GameState sends each event to clients over unreliable RPCs, with a fallback world location resolved server-side; each client then applies its own filters, placements and rendering locally, so two players can legitimately place the same event differently. Set ServerCullDistance and delivery switches to per-client so distant players never pay for pops they cannot see, with one caveat: the component's OnCombatTextReceived delegate only fires on the multicast path, so custom listeners bound to it are skipped once culling is on. For purely local use, call RequestCombatText on the subsystem directly.
Will hundreds of numbers tank my framerate?
Views are pooled and recycled, MaxActiveTexts caps concurrent pops and retires the oldest first, and creation is throttled per frame when the pool is exhausted, and the subsystem never runs on dedicated servers or in editor preview worlds. For ARPG or bullet-hell densities there is an optional batched Niagara renderer that draws every number from one system. Be clear-eyed about that path: it strips rich text, {Prefix} and {Custom}, and it needs a Niagara system and a glyph-atlas material you author yourself against a documented parameter contract, because neither can ship inside the plugin. If it is misconfigured the subsystem falls back to the widget renderer with a warning rather than going blank.
Where can the numbers appear?
Over the target by default, and anywhere else you route them to. A style points at a placement row, and the built-ins cover over the target, below it, three fixed HUD areas, and any UI widget that registered an anchor id: a target frame, or a per-enemy nameplate that resolves to the right one. Whether text follows a moving enemy or stays where the hit landed is a property of the placement, so damage can follow while crits stay put, and off-screen anchors can either hide or clamp to the viewport edge.
Does it ship example content?
Yes. CrimsonCombatText ships demo content (a registry, a motion set, a filter set, a pop widget and a distance curve) with the shipped ini already pointing project settings at them, so numbers pop as soon as you add the GameState component and broadcast.

Make every hit land on screen

CrimsonCombatText is quick to wire up: enable the plugin, add the replication component to your GameState, and broadcast from the server. Demo content ships pre-wired, so numbers pop before you author anything. The Quick Start walks through it.

Pricing

CrimsonCombatText is sold on Fab under Epic’s two standard licenses. Both include the full plugin, source, and updates.

Personal$9.99For individuals & teams under $100K revenueComing to Fab. Join the Discord
Professional$19.99For companies over $100K revenueComing to Fab. Join the Discord