Our Products
Company Info
Community
Contact Us
Address:
Po Box 99900 QZ 905 832, Stn Main,Leduc,
AB, T9E 1A1,
Canada
Phone: + 1 (780) 830 8814
Email: support@dazzlesoftware.org
This article explores common questions that may arise when setting up the Gameplay Ability System (GAS) in your project. Since best practices vary by project, this guide highlights key factors to consider.
Throughout the article, the Lyra Starter Game is frequently referenced as it demonstrates many best practices. Having the Lyra project files on hand can be useful for exploring the code. Additionally, reading through Abilities in Lyra can provide valuable insights into effective implementation.
When working with GAS, it’s important to understand the debugging tools Unreal Engine provides for inspecting GAS values. For an overview, check out this article on GAS Debugging Tools.
When working with GAS it's helpful to know what tools UE provides out of the box to inspect GAS values. Check out this article that presents an overview of GAS Debugging Tools.
Which actors can have an Ability System component?
An Ability System Component (ASC) can be added to any actor that needs modifiable attributes or gameplay tags. This includes controllable entities like characters and vehicles as well as passive objects such as destructible crates or lootable chests.
In most cases, the PlayerState is the best place to add the Ability System Component (ASC). However, depending on your game design, you could also assign it to the PlayerController or Pawn.
Persistence Across Respawns
The ideal placement of the ASC depends on whether you want its state to persist across respawns.
No PlayerState?
In single-player games, you might not have a custom PlayerState class. In this case:
However, in multiplayer games, the PlayerController is not a valid ASC owner since it doesn’t exist on all clients.
For AI-controlled pawns in multiplayer, PlayerStates aren’t always assigned by default. To ensure consistency, consider enabling bWantsPlayerState in the AIController, so each AI bot gets its own PlayerState. Since AIControllers only exist on the server (and are not replicated to clients), they are not suitable for hosting an ASC, whereas PlayerStates are replicated and make managing abilities across players and AI much easier.
OwnerActor vs. AvatarActor in the Ability System Component
The OwnerActor represents the entity that persistently owns the Ability System Component (ASC), while the AvatarActor is the physical representation of that entity in the game world. Your game code should set both by calling InitAbilityActorInfo, which can be done multiple times during the ASC’s lifetime. The ASC stores these references, making them easily accessible when needed.
Using Owner and Avatar in Abilities
When working with an ability blueprint, you can retrieve both the OwnerActor and AvatarActor—whether for the entity executing the ability or its target. Some abilities require an AvatarActor, such as a dodge move that applies momentum to the character. Others, like placing a unit in an RTS game, may not need one at all.
Determining the OwnerActor
The OwnerActor is typically one of the following:
For player-controlled characters, the OwnerActor should be a Pawn, PlayerController, or PlayerState. However, any actor that is ultimately owned (directly or indirectly) by one of these is also valid. Calling GetOwner recursively on the OwnerActor should always lead back to the player’s PC, PS, or Pawn. This ensures that FGameplayAbilityActorInfo::InitFromActor correctly resolves and caches the PlayerController, which is necessary for activating locally predicted abilities.
Determining the AvatarActor
The AvatarActor is usually a Character or Pawn—something with a physical presence in the world. Since some abilities require an AvatarActor, it’s important to handle cases where it might be null. For example, when a player isn’t controlling a Pawn, abilities must account for this scenario to prevent unexpected behavior.
jlknkljhlikh
When Should You Call InitAbilityActorInfo for a Player?
InitAbilityActorInfo must be called separately on both the server and client whenever the OwnerActor or AvatarActor is created or changes during gameplay.
When to Call InitAbilityActorInfo
Handling the PlayerController Dependency in Multiplayer
For multiplayer games, the local player’s PlayerController must be replicated before the ASC can be fully initialized.
To ensure the PlayerController is available before calling InitAbilityActorInfo, you can:
MyPlayerState->GetAbilitySystemComponent()->InitAbilityActorInfo();
Below is example from Lyra OnRep_PlayerState
void ALyraPlayerController::OnRep_PlayerState()
{
Super::OnRep_PlayerState();
BroadcastOnPlayerStateChanged();
// When we're a client connected to a remote server, the player controller may replicate later than the PlayerState and AbilitySystemComponent.
if (GetWorld()->IsNetMode(NM_Client))
{
if (ALyraPlayerState* LyraPS = GetPlayerState<ALyraPlayerState>())
{
if (ULyraAbilitySystemComponent* LyraASC = LyraPS->GetLyraAbilitySystemComponent())
{
// Calls InitAbilityActorInfo
LyraASC->RefreshAbilityActorInfo();
LyraASC->TryActivateAbilitiesOnSpawn();
}
}
}
}
The Gameplay Ability System (GAS) offers three replication modes: full, mixed, and minimal. These determine how much detail about active Gameplay Effects (GE) is replicated to clients.
A good rule of thumb is to use mixed replication mode, unless all clients need access to full effect details.
Regardless of the replication mode, attribute values are still replicated through the Attribute Set if they are marked as replicated.
Gameplay tag counts track the number of sources (such as Gameplay Abilities or Gameplay Effects) contributing to a tag. These counts are for internal tracking only and should not be used directly in game logic.
The Base Value represents an attribute’s raw, unmodified value before any Gameplay Effects (GEs) are applied.
The Current Value is the final, modified value after all active Gameplay Effects have been factored in.
You can find a detailed breakdown of how attributes are calculated here.
Whether to use a single attribute set or multiple depends on how attributes are distributed across different actor types in your project.
Start by listing the actors that will have an Ability System Component (ASC) and attribute sets.
Identify which attributes each actor needs.Group attributes logically common attributes like Health and Max Health can be in one set, while specialized attributes like Weapon Damage might be in another.
For example, in Lyra, attributes are split into multiple sets to keep things modular and efficient.
#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)
If you use the macro on attributes defined in the set, like in the snippet below, it generates convenient getters and setters for the attribute Base value and the FGameplayAttribute definition.
UCLASS()
class ABILITIESLAB_API ULabHealthAttributeSet : public UAttributeSet
{
GENERATED_BODY()
public:
// Current health
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, ReplicatedUsing=OnRep_Health)
FGameplayAttributeData Health;
// Upper limit for health value
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated)
FGameplayAttributeData MaxHealth;
// Current shield
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated)
FGameplayAttributeData Shield;
// Upper limit for shield value
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated)
FGameplayAttributeData MaxShield;
// Damage value calculated during a GE. Meta attribute.
UPROPERTY(VisibleAnywhere)
FGameplayAttributeData Damage;
ATTRIBUTE_ACCESSORS(ULabHealthAttributeSet, Health);
ATTRIBUTE_ACCESSORS(ULabHealthAttributeSet, MaxHealth);
ATTRIBUTE_ACCESSORS(ULabHealthAttributeSet, Shield);
ATTRIBUTE_ACCESSORS(ULabHealthAttributeSet, MaxShield);
ATTRIBUTE_ACCESSORS(ULabHealthAttributeSet, Damage);
}
The value setters are useful for attributes whose changes tend to be permanent, like health. The attribute definition getter is useful for testing during attribute set events, which is the affected attribute like below. The attribute definition getters are static functions so can also be retrieved from elsewhere, like in GameplayEffectExecutionCalculations.
void ULabHealthAttributeSet::PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue)
{
Super::PostAttributeChange(Attribute, OldValue, NewValue);
// GetHealthAttribute() : FGameplayAttribute is generated by ATTRIBUTE_ACCESSORS
// It provides an identifier for the 'Health' attribute.
if (Attribute == ULabHealthAttributeSet::GetHealthAttribute())
{
…
}
}
There are four main ways to attach Attribute Sets to an actor:
If you already know which Attribute Sets a C++ actor class requires, the best approach is to create them as Default Subobjects in the constructor using CreateDefaultSubobject()
.
UObjects
, they reduce network overhead.This method is the most efficient and recommended when the Attribute Set is known at compile time.
AAbilitiesLabCharacter::AAbilitiesLabCharacter()
{
LabAbilitySystemComp = CreateDefaultSubobject<ULabAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
HealthSet = CreateDefaultSubobject<ULabHealthAttributeSet>(TEXT("HealthSet"));
CombatSet = CreateDefaultSubobject<ULabCombatAttributeSet>(TEXT("CombatSet"));
}
ffsfsafasf
PostInitializeComponents()
or BeginPlay()
.AbilitySystemComponent::InitializeComponent()
.Another approach is to add Attribute Sets dynamically when the actor starts play, using AddSet<T>()
.
PostInitializeComponents()
and BeginPlay()
are good places for this.void AAbilitiesLabCharacter::PostInitializeComponents()
{
Super::PostInitializeComponents();
LabAbilitySystemComp->AddSet<ULabHealthAttributeSet>();
LabAbilitySystemComp->AddSet<ULabCombatAttributeSet>();
}
sfsfsfd
Address:
Po Box 99900 QZ 905 832, Stn Main,Phone: + 1 (780) 830 8814
Email: support@dazzlesoftware.org