How-To: Make a Cast-Time Ability

Goal: an ability that winds up for a duration, can be interrupted, only pays its cost when the cast completes, and delivers its effect authoritatively on the server.

Prerequisites
Quick Start.

1. Subclass the cast-time base

Right-click -> Blueprint Class -> `CrimsonGameplayAbility_WithCastTime`.

Verify
The new class exposes a Cast Time category in Class Defaults.

2. Set the cast time and commit point

ECrimsonAbilityCommitTime decides when the cost is actually paid.

ValueCost is paid
On ActivationImmediately, before the cast runs. Interrupting still costs the player.
On Cast CompletedOnly if the cast finishes. Interruption is free.
On Cast Completed and on InterruptedOn completion, and also when interrupted.
Cast time can be dynamic
Override Update Ability Cast Time (a BlueprintNativeEvent) to compute the duration from attributes - haste, a stack count, whatever. Read the resolved value back with Get Calculated Cast Time.
Verify
Activate the ability and confirm the cost is only deducted at the point you chose.

3. Deliver the effect on the server

The base class fires each lifecycle point twice: a (Cosmetic) event that runs on the server AND on the predicting client, and an (Authority) event that runs only where HasAuthority is true. Spawning a projectile or applying a gameplay effect belongs in the authority event.

EventRuns onUse for
On Cast Started (Cosmetic)Server and predicting clientCast bar, wind-up VFX, sound.
On Cast Completed (Cosmetic)Server and predicting clientRelease VFX, camera shake.
On Cast Interrupted (Cosmetic)Server and predicting clientFizzle VFX, failure sound.
On Cast Started (Authority)Server onlyReserve a target, spawn a wind-up actor.
On Cast Completed (Authority)Server onlySpawn the projectile, apply the gameplay effect.
On Cast Interrupted (Authority)Server onlyRefund a resource, clean up server state.
Spawning in the cosmetic event desyncs
On Cast Completed (Cosmetic) runs on both machines. Spawning an actor there produces two of them on a listen server and a client-only ghost on a remote client. Anything that mutates replicated state belongs in the (Authority) event.

Right-click in the ability graph -> Add Event -> On Cast Completed (Authority), then Spawn Actor from Class (Class = your projectile; Transform = Get Avatar Actor from Actor Info -> GetActorTransform).

Put the muzzle flash and sound on On Cast Completed (Cosmetic) instead, so the caster sees them without waiting for the server.

The server owns the cast clock
Both machines run their own cast timer, but only the server's decides when the cast completes, when cost and cooldown are charged for the On Cast Completed commit modes, and when the ability ends. The client's timer drives the cosmetic events only, so a client cannot shorten or cancel its own cast on the server.
Verify
Play As Client with 2 players. Exactly one projectile spawns per cast and it exists on both machines. Two on the host, or one only the caster can see, means the spawn is still in the cosmetic event.

4. Handle interruption

The plugin tags casting state for you: Ability.State.Casting while the cast runs, and Ability.State.Interrupted when it breaks. Watch those to react to whether a cast is happening - dimming a reticle, blocking another input, playing a stagger. They carry no duration, so they are not what a progress bar reads; step 5 covers that.

Use Wait Gameplay Tag Add / Wait Gameplay Tag Remove on Ability.State.Casting to run cosmetics for as long as any cast is up.

Verify
Interrupt a cast. The tag is removed and, with On Cast Completed, no cost was paid.

5. Drive a cast bar

The base class fires FCrimsonAbilityProgressMessage through the ASC's OnAbilityProgress event (native OnAbilityProgressNative, channel Message.Ability.Progress) when a timed segment starts and ends. A HUD widget binds it on its own player's ASC.

FieldMeaning
InstigatorThe avatar casting. Filter on this - a split-screen partner shares the process.
AbilitySpecHandleWhich ability. Key your active bar on it.
AbilityIdentifierTagStable identity, for per-ability styling or an icon lookup.
StartWorldTimeWorld time the cast began. Interpolate against this.
DurationTotal seconds. Zero or less means indeterminate - an infinite channel. Never divide by it.
PhaseStarted, Completed, or Interrupted.
KindCast or Channel, so one widget can style both.
Two messages, not a stream
You get one message at the start and one at the end - there is no per-tick broadcast. ModifiedCastTime is computed once in ActivateAbility and cannot change mid-cast, so interpolating locally from StartWorldTime against the world clock is not an approximation - it is exactly correct, and smoother than any broadcast rate would be.

Fill = (World Time - StartWorldTime) / Duration, clamped to 0..1.
Your own casts only
This is a local, in-process broadcast. It reaches the server and the predicting client, which covers the player's own bar - it does not cross the wire, so it cannot show another player's or an NPC's cast.

That failure is invisible on a listen-server host, whose HUD shares a process with the server. Always check enemy-facing UI from a remote client. Showing someone else's cast needs replicated cast state on the avatar.

In your HUD widget: Event Construct -> Get Ability System Component (owning player pawn) -> Cast To Crimson Ability System Component -> Bind Event to On Ability Progress. In the event, branch on the message's Phase: Started stores StartWorldTime and Duration and shows the bar; Completed and Interrupted hide it. On Tick, set the fill from (Get Game Time In Seconds - StartWorldTime) / Duration.

Ignore a Completed or Interrupted whose AbilitySpecHandle does not match the one you stored - otherwise a channel ending clears a cast that started after it.

Reference implementation (CrimsonCore - requires all Crimson plugins)
CrimsonCore ships UCrimsonCoreCastBarWidget with this already built, plus a finish-animation state machine - the bar stays visible after the cast ends so an interrupted animation can play, and a new cast preempts it. Study it if you have the suite; the code above is the standalone equivalent.
Verify
Cast a 2-second ability: the bar fills smoothly and hides on completion. Set the cast time to 0: no bar flashes at all. Interrupt a cast: the bar hides immediately. Then start a channel, start a cast over it, and stop the channel - the cast bar must survive.

See also

  • How-To: Use Toggle, Charges and Channeling
  • Concept: Ability Subclasses