Create a movement speed boost using AttributeSets and GAS.

Hi there,

I’ve set-up my GAS. Everything works perfectly, I have an attribute set (PlayerDefaultAttributeSet) with a speed attribute :

#pragma once

#include "CoreMinimal.h"
#include "AttributeSet.h"
#include "AbilitySystemComponent.h"
#include "PlayerDefaultAttributeSet.generated.h"

#define ATTRIBUTE_ACCESORS(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)


/**
 * 
 */
UCLASS()
class PROJECTB_API UPlayerDefaultAttributeSet : public UAttributeSet
{
	GENERATED_BODY()

	UPlayerDefaultAttributeSet();

public:
	ATTRIBUTE_ACCESORS(UPlayerDefaultAttributeSet, Health);
	ATTRIBUTE_ACCESORS(UPlayerDefaultAttributeSet, MaxHealth);

	ATTRIBUTE_ACCESORS(UPlayerDefaultAttributeSet, Speed);

	UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_Health, Category= "Attributes", Meta = (AllowPrivateAccess = true))
	FGameplayAttributeData Health;

	UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_MaxHealth, Category = "Attributes", Meta = (AllowPrivateAccess = true))
	FGameplayAttributeData MaxHealth;

	UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_Speed, Category = "Attributes", Meta = (AllowPrivateAccess = true))
	FGameplayAttributeData Speed;
	
	UFUNCTION()
	virtual void OnRep_Health(const FGameplayAttributeData& OldHealth);
	UFUNCTION()
	virtual void OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth);
	UFUNCTION()
	virtual void OnRep_Speed(const FGameplayAttributeData& OldSpeed);

	virtual void PreAttributeBaseChange(const FGameplayAttribute &Attribute, float &NewValue) const override;
	virtual void PreAttributeChange(const FGameplayAttribute &Attribute, float &NewValue) override;

	virtual void ClampAttributeOnChange(const FGameplayAttribute &Attribute, float &NewValue) const;
};

And I have my ability system correctly setup on the character script :

AProjectBCharacter::AProjectBCharacter()
{
	// Set size for collision capsule
	GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
		
	// Don't rotate when the controller rotates. Let that just affect the camera.
	bUseControllerRotationPitch = false;
	bUseControllerRotationYaw = false;
	bUseControllerRotationRoll = false;

	// Configure character movement
	GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input...	
	GetCharacterMovement()->RotationRate = FRotator(0.0f, 500.0f, 0.0f); // ...at this rotation rate

	// Note: For faster iteration times these variables, and many more, can be tweaked in the Character Blueprint
	// instead of recompiling to adjust them
	GetCharacterMovement()->JumpZVelocity = 700.f;
	GetCharacterMovement()->AirControl = 0.35f;
	GetCharacterMovement()->MinAnalogWalkSpeed = 20.f;
	GetCharacterMovement()->BrakingDecelerationWalking = 2000.f;

	// Create a camera boom (pulls in towards the player if there is a collision)
	CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
	CameraBoom->SetupAttachment(RootComponent);
	CameraBoom->TargetArmLength = 400.0f; // The camera follows at this distance behind the character	
	CameraBoom->bUsePawnControlRotation = true; // Rotate the arm based on the controller

	// Create a follow camera
	FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("ThirdPersonCamera"));
	FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation
	FollowCamera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm

	// Create the ability system component
	AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("AbilitySystemComp"));
	AbilitySystemComponent->SetIsReplicated(true);
	AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Minimal);

	// Create attribute set
	AttributeSet = CreateDefaultSubobject<UPlayerDefaultAttributeSet>(TEXT("AttributeSet"));


	// Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character) 
	// are set in the derived blueprint asset named ThirdPersonCharacter (to avoid direct content references in C++)
}

I want my character movement speed GetCharacterMovement()->MaxWalkSpeed to depends on the speed attribute. That way, when I create a gameplay effect that adds 30% to the speed attribute, the change are applied to the maxwalkspeed of the character.

I have two question :

  • How do I make it so the MaxWalkSpeed depends on the speed attribute ?
  • What do I need to write to assign a gameplay effect to self (also remove it), with the gameplay effect being a blueprint I’ve already created ?

(For the former question, I have set up a sprint button that ideally would apply a gameplay effect modifiers to the speed attribute on pressed, and remove the gameplay effect on released)

ChatGPT doesn’t provide any meaningful answers and the solution to create a delegate found here : https://github.com/tranek/GASDocumentation?tab=readme-ov-file#concepts-a just straight up doesn’t work.

Thank you in advance

2 Likes

Use PostAttributeChange in Attribute set to adjust MaxWalkSpeed :slight_smile:

void UMovementAttributeSet::PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue)
{
	Super::PostAttributeChange(Attribute, OldValue, NewValue);

	if (FGameplayAbilityActorInfo* ActorInfo = GetActorInfo())
	{
		if (Attribute == GetMovementSpeedAttribute())
		{
			if (UCharacterMovementComponent* CharacterMovementComponent = Cast<UCharacterMovementComponent>(ActorInfo->MovementComponent))
			{
				CharacterMovementComponent->MaxWalkSpeed = NewValue;
			}
		}
	}
}

With that, whenever attribute changes, your value is updated

1 Like

Extending what @Gawron10001 said you can bind of attributes changes in you character too.

void AProjectBCharacter::BeginPlay()
{
	AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(AttributeSet->GetMaxWalkSpeedAttribute()).AddUObject(this, &AProjectBCharacter::OnMaxWalkSpeed);
}
1 Like

You should probably do it on the character movement component, using your own custom class that extends GetMaxSpeed()
That way you can do any processing that you want, like detect root/immobilize effects for example through tags

You just have to get the ability system of your character and fetch the appropriate data

1 Like