Weapons · Updated May 14, 2026

Weapon Fire Ability

Weapon Fire Ability


#Overview

USLGameplayAbility_Fire is the C++ base class for all fire abilities. It handles the full shot loop — timing, tracing, damage, recoil, and cue dispatch. BP subclasses implement the two cosmetic events and configure the ability tags.

Net execution policy: Local Predicted

The ability runs immediately on the owning client without waiting for the server. The server also runs ExecuteFire (listen-server host) or receives the shot via Server_ProcessShots (dedicated server / remote clients).


#Creating a Fire Ability BP

  1. Create a new Blueprint, parent class SLGameplayAbility_Fire
  1. Name it BP_GA_SL_<WeaponName>_PrimaryFire (or SecondaryFire)
  1. In Class Defaults, set:
SettingValueNotes
bIsPrimaryFiretrue for primary, false for secondary
Required Weapon ClassBP_WeaponActor_<WeaponName>Required. Prevents all granted fire abilities from activating on the same event — only the equipped weapon's ability runs.
Net Execution PolicyLocal Predicted
Instancing PolicyInstanced Per Actor
Ability TagsSLTags.Abilities.PrimaryFire
Activation Owned TagsSLTags.States.Weapon.FiringApplied while the ability is active. bIsFiring reads this tag — drive fire and pump-cycle animations from it.
Activation Blocked TagsSLTags.States.Weapon.FiringPrevents re-activation while the tag is on the ASC — blocks rapid-fire during PostFireDelay on single-shot weapons.
Block Abilities with Tag(leave empty)Do NOT use this for States.Weapon.Firing. In UE5.7, BlockAbilitiesWithTag bleeds into the main tag container and causes CommitAbility to fail on unrelated abilities. Use Activation Blocked Tags instead.
Triggers[0] → TagSLTags.Events.Weapon.PrimaryFire
Triggers[0] → SourceGameplay Event
  1. Set FireAbilityClass on the weapon data asset's PrimaryFireMode to this BP class — the weapons component grants it when the weapon is equipped.

#Single-shot pump/burst delay

Set PostFireDelay (seconds) on FSLWeaponFireMode in the weapon data asset. The fire ability stays alive for that duration after the shot fires — SLTags.States.Weapon.Firing remains on the ASC and bIsFiring is true, giving the anim blueprint time to play the pump or bolt-cycle animation.

Activation Blocked Tags = SLTags.States.Weapon.Firing is the only thing preventing re-fire during the pump window. Without it the player can spam the trigger and the ability re-activates each press, restarting the timer and breaking the pump animation. The Activation Owned Tags row above grants the tag; this row blocks re-entry on it. Both are required. No cooldown GE needed.


#Execution Flow


Input fires → SendGameplayEvent(PrimaryFire tag)

│

└── ActivateAbility

      │

      ├── CommitAbility (cost/cooldown check) → fail → EndAbility

      │

      ├── ExecuteFire ← first shot immediately

      │

      └── [FullAuto]    SetTimer(FireInterval) → ExecuteFire each interval

            [SingleShot] PostFireDelay > 0 → SetTimer(PostFireDelay) → EndAbility

                         PostFireDelay == 0 → EndAbility immediately

Releasing the fire input cancels the ability, which clears the timer via EndAbility.

#ExecuteFire — per-shot logic


ExecuteFire

│

├── GetPlayerViewPoint → CamLoc, CamRot, AimDir

│

├── MuzzleSocketName → MuzzleLoc (FP weapon mesh socket, falls back to CamLoc)

│

├── [IsLocallyControlled]   DispatchLocalCosmetics:

│       OnLocallyPredictedShotFired(MuzzleLoc, AimDir)

│       WeaponsComponent::ApplyRecoilKick(FireMode)

│       WeaponActor::OnWeaponFired(MuzzleLoc, AimDir)

│   (Skipped on the dedicated server when running a remote client's predicted activation.)

│

├── [HasAuthority && FireCueTag valid]

│     ASC::ExecuteGameplayCue(FireCueTag, {Location=MuzzleLoc, Normal=AimDir})

│     (GAS multicasts to all clients — remote players see muzzle flash / hear fire sound)

│

├── [HasAuthority]  ApplyAmmoDecrement(FireMode)  ← once per trigger pull

│

└── Pellet loop  (1× for bullets, N× for shotgun via PelletCount)

    │ PelletDir = ComputeFireDirection(AimDir, cone)

    │ TraceEnd  = CamLoc + PelletDir * MaxRange

    │

    ├── [HasAuthority && IsLocallyControlled] ── Listen-server host path ──

    │     Hitscan  → LineTrace(ECC_Visibility)

    │                  → ApplyGameplayEffectSpecToTarget (BaseDamage via SetByCaller)

    │                  → ExecuteGameplayCue(ImpactCueTag) on hit

    │                  → OnProjectileHitPredicted(Hit) — host's only local impact path

    │     Projectile → SpawnActor(ProjectileClass)

    │

    └── [!HasAuthority] ── Remote-client path ──

          TracePredictedHit:

            LineTrace(ECC_Visibility) → OnProjectileHitPredicted(Hit)   ← predicted hit FX

          PendingPellets.Add(shot info)



(After the loop, the remote client flushes all pellets via WC->QueueShotsForValidation(PendingPellets) — one RPC per trigger pull regardless of pellet count.)

Both authority and predicted traces use ECC_Visibility so they agree on whether a wall blocks the shot.


#BP Events to Implement

#Weapon Actor Events

The fire ability also calls two BlueprintImplementableEvents directly on the equipped ASLWeaponActor:

EventWhenUse
OnWeaponFired(MuzzleLoc, FireDir)Every shot, local client onlyMaterial pulse, muzzle FX, per-shot sounds owned by the weapon actor
OnWeaponStoppedFiring()EndAbility, local client onlyStop looping effects, reset material state

Implement these in the weapon actor BP subclass (e.g. BP_WeaponActor_AssaultRifle). They fire on the same frame as OnLocallyPredictedShotFired.


#OnLocallyPredictedShotFired(MuzzleLocation, FireDirection)

Called every shot, owning client only, with zero latency.

Use this for effects the local player sees regardless of view — it fires for both FP and TP perspectives.


Event OnLocallyPredictedShotFired (MuzzleLocation, FireDirection)

│

├── [FP] Play Montage on FP character mesh / FP weapon mesh (local, not replicated)

│

├── [FP] Spawn Niagara muzzle flash at MuzzleLocation

│

└── [FP] Play fire sound (local 2D sound, not positional)

Do NOT duplicate effects the FireCueTag cue already handles. The cue covers all remote clients. The local player is intentionally excluded from the cue (CueParams.Instigator check in the GC Blueprint) to prevent double-playing.

#OnProjectileHitPredicted(Hit)

Called when the local prediction trace hits something. Use this for zero-latency impact feedback before the server confirms. Fires on both non-authority clients AND the listen server host — the ImpactCueTag Gameplay Cue excludes locally controlled pawns, so this is the only impact path for the host player.

See WeaponHitFX.md for the full two-path impact system.


Event OnProjectileHitPredicted (Hit)

│

├── Spawn Niagara impact system at Hit.ImpactPoint

│     Rotation: MakeRotFromZ(Hit.ImpactNormal)

│

└── Play local impact sound at Hit.ImpactPoint

The ImpactCueTag Gameplay Cue (fired by Server_ProcessShots) handles confirmed impact FX on all other clients. The owning client uses this predicted path instead.

#Fire Mode Data Asset Setup

FieldValue
bEnabledtrue
FireTypeHitscan or Projectile
FireModeFullAuto or SingleShot
FireAbilityClassBP_GA_SL_<WeaponName>_PrimaryFire
RoundsPerMinutee.g. 600
MaxAmmoe.g. 32 — weapon actor initializes CurrentAmmo to this on spawn
AmmoDecrementEffectGE_SL_AmmoDecrement — applied per shot on authority to decrement USLAmmoAttributeSet.CurrentAmmo
BaseDamagee.g. 20
MaxRangee.g. 15000
MuzzleSocketNameSocket on the FP weapon mesh, e.g. Muzzle
FireCueTagGameplayCues.Weapon.<WeaponName>.PrimaryFire
ImpactCueTagGameplayCues.Weapon.<WeaponName>.Impact

#Full-Auto vs Single-Shot

FireModeBehavior
FullAutoTimer fires ExecuteFire every 60 / RoundsPerMinute seconds while the ability is active. Releasing fire cancels the ability, clearing the timer.
SingleShotAfter the first shot, if PostFireDelay > 0, the ability stays alive for that many seconds (driving bIsFiring for pump/cycle animations), then ends. If PostFireDelay == 0, EndAbility is called immediately.

#Shot Validation (Hitscan, Remote Clients)

After the full pellet loop, remote clients call QueueShotsForValidation once per trigger pull with every pellet from that shot. The weapons component batches across trigger pulls too and sends them to the server via Server_ProcessShots — so a single shotgun blast is one RPC, not eight.

The server re-traces each shot, applies damage, fills HitLocation / HitNormal / bHitPawn into FSLShotInfo, then multicasts confirmed hits via Multicast_OnShotsConfirmedOnShotsConfirmed BP event on all clients.

MaxUnvalidatedShots and ShotValidationInterval on USLWeaponsComponent control batching behavior. Default is flush every shot.

See WeaponHitFX.md for the impact cue flow that runs inside Server_ProcessShots.


#Replication Summary

EffectWhoMechanism
FP fire anim / muzzle flash (local)Owning client onlyOnLocallyPredictedShotFired — direct spawn
TP fire animAll clientsPlayMontageAndWait on the TP mesh (GAS replication)
Muzzle flash / fire sound (remote)All other clientsFireCueTag Gameplay Cue — GAS multicast
Predicted impact (pre-confirm)Owning client onlyOnProjectileHitPredicted — direct spawn
Confirmed impactAll other clientsImpactCueTag Gameplay Cue — GAS multicast from Server_ProcessShots
DamageServer onlyApplyPointDamage (listen host) or Server_ProcessShots (clients)

#Adding a Fire Ability for a New Weapon

  1. Create BP_GA_SL_<WeaponName>_PrimaryFire (parent: SLGameplayAbility_Fire)
  1. Set Class Defaults per the table above
  1. Implement OnLocallyPredictedShotFired — FP muzzle flash, local sound, FP fire montage
  1. Implement OnProjectileHitPredicted — predicted impact VFX/SFX
  1. Add GameplayCues.Weapon.<WeaponName>.PrimaryFire tag in Project Settings → GameplayTags
  1. Create GC_SL_<WeaponName>_PrimaryFire (GameplayCueNotify_Burst) — set its tag, wire muzzle flash + TP fire sound with IsLocallyControlled guard to skip the owning client
  1. Set FireAbilityClass, FireCueTag, and ImpactCueTag on the weapon data asset's PrimaryFireMode