Tutorial: Your First 60 Minutes with Gameplay Ability System

In this tutorial we’ll go through many concepts of the Gameplay Ability System (GAS) and use them in a practical way. GAS is a framework for organizing gameplay values and behaviors for players, NPCs, items and interactables in reusable assets. GAS is used in Epic’s own games, so it’s a feature rich and battle tested system.

https://dev.epicgames.com/community/learning/tutorials/8Xn9/unreal-engine-epic-for-indies-your-first-60-minutes-with-gameplay-ability-system

So cool and thank you. My course is not there yet but will be soon. Currently learning basic attack animation and 3D modelling but I already know that I will be using the tip and tricks from this link you’ve provided. Thanks again.

1 Like

I had some issues with Creating a Targeted Ability that I resolved. Posting incase others have similar issues.

Before we get into it. I set my Primary Ability [Q] to GA_TargetingBase. That step was implied in the guide, but not explicitly stated.

First issue was a crash due to null player controller

void AGameplayAbilityTargetActor_Trace::AimWithPlayerController(const AActor* InSourceActor, FCollisionQueryParams Params, const FVector& TraceStart, FVector& OutTraceEnd, bool bIgnorePitch) const
{
	if (!OwningAbility) // Server and launching client only
	{
		return;
	}

	APlayerController* PC = OwningAbility->GetCurrentActorInfo()->PlayerController.Get();
	check(PC); // <- crash happened here.

Googling told me that a stale PC reference was not uncommon, and to add this code.

void ATFirst60GASCharacter::PossessedBy(AController* NewController)
{
	Super::PossessedBy(NewController);
	LabAbilitySystemComp->RefreshAbilityActorInfo();
}

void ATFirst60GASCharacter::UnPossessed()
{
	Super::UnPossessed();
	LabAbilitySystemComp->RefreshAbilityActorInfo();
}

That stopped the crash. Someone else commented that committing the ability also works, but I didn’t see that until latter.

The second issue I encountered was I didn’t see any debug lines from the trace. Setting some breakpoints told me that the trace was running and hitting what was in front of me. Pressing F8 and flying around it was obvious that my character was just obstructing the debug lines from view. So I just moved on and created the area reticle which works fine.

1 Like

Just finished. This was awesome!

The follow up guides, debugging and best practices, were a great idea. It doesn’t pollute the main guide keeping it focused, while at the same time they are great stand alone articles. I don’t think I’ve seen another “Your First 60 Minutes with __” do that. Good thinking.

Keep the content coming!

Thanks! I’ve updated the guide to add a section ‘Setting the Owner and Avatar’ to call out what InitAbilityActorInfo is for, with a little note for multiplayer projects where indeed ensuring the client’s ASC has cached the PC correctly takes some extra work.

I’ve also added some screenshots + more description on testing GA_TargetedBase with the print string. Rereading that section I do gloss over how to give the ability. Thanks for the feedback!

1 Like

Is it mandotory for actors that use the ability system component to implement IAbilitySystemInterface if they have the component public like the AbilitiesLabCharacter in the guide?

UCLASS()
class AAbilitiesLabCharacter : public ACharacter
{
	GENERATED_BODY()
 
public:
	AAbilitiesLabCharacter();
	
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Abilities)
	TObjectPtr<class ULabAbilitySystemComponent> LabAbilitySystemComp;
};

I saw that the interface was mentioned in Gameplay Ability System - Best Practices for Setup | Tutorial when talking about multiple asc’s. Also noticed that characters in the ActionRPG sample project and the player state in LyraStarterGame sample project implement it.

P.S. Thanks for the guide!

It’s not mandatory, but is recommended. There is a static helper function

UAbilitySystemGlobals::GetAbilitySystemComponentFromActor

that gets the ASC from any actor. It’s called internally in GAS for example in GameplayAbility setup and in blueprint callable static functions from AbilitySystemBlueprintLibrary and GameplayCueFunctionLibrary.

That helper function will call the interface function if the actor implements it, and otherwise will search for an AbilitySystemComponent on the actor via FindComponentByClass. That fallback step is enabled for all GAS code, but if you call GetAbilitySystemComponentFromActor yourself you can disable that step.

	const IAbilitySystemInterface* ASI = Cast<IAbilitySystemInterface>(Actor);
	if (ASI)
	{
		return ASI->GetAbilitySystemComponent();
	}

	if (LookForComponent)
	{
		// Fall back to a component search to better support BP-only actors
		return Actor->FindComponentByClass<UAbilitySystemComponent>();
	}

When an actor implements the interface, finding the ASC will be more efficient since your class’s implementation is likely O(1) while the fallback is O(n) i.e. with many components it’ll take longer to find the ASC. I’ll update both the Best Practices doc and this 60 minutes tutorial (to inspire good habits). Thank you for bringing this up. :slight_smile:

1 Like

Thank you for the tutorial, it is very helpful!

Quick question about the IAbilitySystemInterface
Implementing it requires us to override the function
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
which returns a UAbilitySystemComponent*
But in the Character class the tutorial defines TObjectPtr<class ULabAbilitySystemComponent> LabAbilitySystemComp, which cannot be returned as UAbilitySystemComponent*

In general, should we add ASC to the actor as the default UAbilitySystemComponent or our derived ULabAbilitySystemComponent?