// Copyright (c) 2026 Reignance Studios. MIT License, see LICENSE. #include "RSExampleDashAbility.h" #include "GameFramework/Character.h" URSExampleDashAbility::URSExampleDashAbility() { // Client runs it at once and asks the server; the server confirms or rolls back. NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::LocalPredicted; // One instance per actor: no new UObject per activation, and state survives // between the predicted and the confirmed run. InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor; // Set in the Blueprint child or here: // CostGameplayEffectClass = GE_Cost_Dash (Instant, Stamina -25) // CooldownGameplayEffectClass = GE_Cooldown_Dash (HasDuration 1.5 s, grants Cooldown.Dash) } void URSExampleDashAbility::ActivateAbility( const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) { // Commit first, inside the activation's prediction window. Committing after a // latent task (a delay, a montage wait, target data) runs outside that window: // the client cannot predict the cost or cooldown, so the UI jumps when the // server value lands. if (!CommitAbility(Handle, ActorInfo, ActivationInfo)) { EndAbility(Handle, ActorInfo, ActivationInfo, true, true); return; } if (ACharacter* Character = Cast(ActorInfo->AvatarActor.Get())) { // Runs on the predicting client and on the server. Kept simple on purpose: // launch timing can differ by a frame and cause a small correction. For a // shipped dash, use a root motion source task or a custom CMC saved move. const FVector Direction = Character->GetActorForwardVector(); Character->LaunchCharacter(Direction * DashStrength, true, false); } EndAbility(Handle, ActorInfo, ActivationInfo, true, false); }