V.3.0.0

Slide Ability – Advanced Movement System (AMS) Plugin in Lyra UE5

Learn how the Slide Ability is implemented using the Advanced Movement System (AMS) plugin in Lyra (UE5).

Introduction

This tutorial explains how sliding is handled through Gameplay Abilities, with a clean separation between input handling, movement logic, and gameplay conditions like blocking.

Sprint feature demonstration in Lyra using Unreal Engine and Gameplay Ability System

Configuration Variables

Several key variables used to control sliding are exposed directly in the CharacterMovementComponent. They allow you to fine-tune sliding behavior to match the desired gameplay style.

Sprint feature demonstration in Lyra using Unreal Engine and Gameplay Ability System

Gameplay Ability

To handle sliding in our system, we have a dedicated Gameplay Ability named ULyraGameplayAbility_Slide, which is very simple and inherits from the ULyraGameplayAbility class provided by the Lyra framework.

This ability contains two methods:

  • StartSliding

  • StopSliding

These methods are called when the ability is activated or ended, and they simply delegate the action to the ACharacter, which already contains the StartSliding and StopSliding functions as part of its own movement logic.

/**
 * ULyraGameplayAbility_Slide
 *
 * Gameplay ability used for character sliding.
 */
UCLASS()
class LYRAGAME_API ULyraGameplayAbility_Slide : public ULyraGameplayAbility
{
	GENERATED_BODY()

public:
	ULyraGameplayAbility_Slide(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());

protected:
	virtual bool CanActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags, FGameplayTagContainer* OptionalRelevantTags) const override;
	virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override;

protected:
	/** Indicates that the player intends to slide */
	UFUNCTION(BlueprintCallable, Category = "Lyra|Ability")
	void StartSlide();

	/** Indicates that the player intends to stop sliding */
	UFUNCTION(BlueprintCallable, Category = "Lyra|Ability")
	void StopSliding();
	
};

In the constructor of this ability, we simply call the parent class constructor. This constructor sets up the ability to allow immediate client-side activation. At the same time, a request is sent to the server, which then validates or cancels the activation based on the game’s rules.

ULyraGameplayAbility::ULyraGameplayAbility(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	ReplicationPolicy = EGameplayAbilityReplicationPolicy::ReplicateNo;
	InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor;
	NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::LocalPredicted;
	NetSecurityPolicy = EGameplayAbilityNetSecurityPolicy::ClientOrServer;

	ActivationPolicy = ELyraAbilityActivationPolicy::OnInputTriggered;
	ActivationGroup = ELyraAbilityActivationGroup::Independent;

	bLogCancelation = false;

	ActiveCameraMode = nullptr;
}

Although the Gameplay Ability System (GAS) is meant to handle the activation and deactivation of abilities, it should be noted that the CharacterMovementComponent can change the character's movement mode in certain situations, for example when the character falls off a cliff. In this case, the movement mode automatically switches to MOVE_Falling, which physically stops the slide by adapting the physics to falling.

In my implementation, I assume that the CharacterMovementComponent handles this physical movement mode change and the associated cleanup, while I override the OnSlideInterrupted method to send a Gameplay Event to the Ability System. This allows the Ability to become aware of the interruption and properly terminate itself via GAS.

void ULyraCharacterMovementComponent::OnSlideInterrupted()
{
	SendGameplayEventToSelf(LyraGameplayTags::GameplayEvent_SlideInterrupted);
}

That's all for the C++ setup. Now let's move on to the Blueprint part.

In our case, we are using an ability Blueprint named GA_Hero_Slide, which inherits from the ULyraGameplayAbility_Slide class defined in C++. This ability calls the StartSliding method when activated, and StopSliding when the ability ends, through a call to EndAbility.

This Ability Blueprint also listens to the GameplayEvent.Slide.Interrupted event emitted by the CharacterMovementComponent. When this event is received, the ability calls EndAbility to properly terminate the ability through the GAS system.

Blueprint of the GA_Hero_Sprint ability showing the activation and sprint start logic
Blueprint logic for handling Gameplay Event InterruptSprint and stopping the sprint ability

Triggering Ability via Player Input

To bind an Ability to player inputs in Lyra, you need to follow two main steps:

Define the input in the Input Data Asset

In the InputData_Hero Data Asset, I added a new entry to the AbilityInputActions list.

This entry includes:

  • An InputAction, which corresponds to the specific input action.

  • An InputTag, which serves as a semantic identifier to link this input to the corresponding Ability in the system.

InputData_Hero Data Asset showing the AbilityInputActions configuration for sprint input

Associate the Ability in the Ability Set Data Asset

Then, in the AbilitySet_ShooterHero Data Asset, I added a new entry to the GrantedGameplayAbilities list.

This entry contains:

  • The Ability we want to grant.

  • The level of this Ability.

  • The corresponding Input Tag, which links the input to the activation of the Ability.

AbilitySet_ShooterHero Data Asset showing GrantedGameplayAbilities configuration for Sprint Ability

Camera Offset on the Z Axis During Slide

To visually support the lowered posture of the character while sliding, the camera is also shifted on the Z axis. This behavior follows the same logic used for crouching, using the SetTargetCrouchOffset method inside UpdateForCrouchTarget.


void ULyraCameraMode_ThirdPerson::UpdateForTarget(float DeltaTime)
{
	if (const ALyraCharacter* TargetCharacter = Cast<ALyraCharacter>(GetTargetActor()))
	{
		if (TargetCharacter->IsCrouched())
		{
			const ACharacter* TargetCharacterCDO = TargetCharacter->GetClass()->GetDefaultObject<ACharacter>();
			const float CrouchedHeightAdjustment = TargetCharacterCDO->CrouchedEyeHeight - TargetCharacterCDO->BaseEyeHeight;

			SetTargetCrouchOffset(FVector(0.f, 0.f, CrouchedHeightAdjustment));

			return;
		}

		UAMS_CharacterMovementComponent* CharacterMovement = Cast<UAMS_CharacterMovementComponent>(TargetCharacter->GetCharacterMovement());

		if (CharacterMovement && CharacterMovement->IsSliding())
		{
			const AAMS_Character* TargetCharacterCDO = TargetCharacter->GetClass()->GetDefaultObject<AAMS_Character>();
			const float SlidedHeightAdjustment = TargetCharacterCDO->SlidedEyeHeight - TargetCharacterCDO->BaseEyeHeight;

			SetTargetCrouchOffset(FVector(0.f, 0.f, SlidedHeightAdjustment));

			return;
		}
	}

	SetTargetCrouchOffset(FVector::ZeroVector);
}

Horizontal Camera Offset During Slide

To move the camera horizontally during the acceleration caused by sliding, we reuse the SetTargetAccelerationOffset method, which was originally implemented for sprinting. We simply modify the offset value applied in the UpdateForAccelerationTarget method by checking if the character is sliding via CharacterMovement->IsSliding():

void ULyraCameraMode_ThirdPerson::UpdateForAccelerationTarget(float DeltaTime)
{
	if (const ALyraCharacter* TargetCharacter = Cast<ALyraCharacter>(GetTargetActor()))
	{
		if (UAMS_CharacterMovementComponent* CharacterMovement = Cast<UAMS_CharacterMovementComponent>(TargetCharacter->GetCharacterMovement()))
		{
			if (CharacterMovement->bIsSprinting)
			{
				SetTargetAccelerationOffset(100.f);
				return;
			}
			if (CharacterMovement->IsSliding())
			{
				SetTargetAccelerationOffset(150.f);
				return;
			}
		}
	}

	SetTargetAccelerationOffset(0.f);
}

In this example, an offset of 150.f is applied during the slide, creating a more pronounced camera pull-back effect than sprinting.