Announcement

Collapse
No announcement yet.

GameplayAbilities and you.

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

    [TUTORIAL] GameplayAbilities and you.

    Hello reader, I request you to visit the wiki page based on this forum post instead of trying to follow steps and take information from here. It is much more likely to be up to date and more complete than this ol' wall of text here.

    Introduction

    Hello, my name is KZJ(although my pals call me Kaz, and you may do so too), and I am here to tell you about the wondrous world of GameplayAbilities, how powerful they can be, how to properly make use of them and how they can make your life easier, if your game is in need of a powerful skill, buff and attribute system that is both easy to extend and crazy-efficient to replicate, so people wishing to develop a multiplayer RPG with a lot of skills/classes or perhaps even a MOBA may want to listen closely. You can use this system for pretty much any game you want, but it isn't the easiest to comprehend, quite big and may get a little in your way the further you stray too far away from this multiplayer RPG ideal, so not every game will get the same mileage out of it.

    Well, that sounds like a dream, but what are GameplayAbilities, and where do I get it? Well, first of all, GameplayAbilities is a code module that used to be integrated into UE4's source, but since this current version(4.15, that is, people from the future) has been moved into a separate plugin that is delivered alongside the UnrealEngine, so that it may not take away space in your games if they do not make use of the system. This system does not actually originate as a built-in engine feature, but has, in fact, been kindly left in there from the developers of Paragon and Fortnite for third parties to enjoy. Unfortunately, due to these unique circumstances, the module as a whole is quite messy, poorly(read: barely at all, your best bet are code comments and even those are only there like half the time) documented, and rarely updated and cleaned up.

    It is also not 100% exposed to blueprints, partially, but not entirely, due to a lot of the system abusing a lot of engine trickery and magic to work as well as they do, so if you never worked with C++ in the context of UE4, you may want to turn back and maybe do a little tutorial on that now, because this tutorial will make for a poor first learning experience.

    In other words, it is a total flippin' pain in the buttocks to wrap your head around, but that's where I come in to help ya. You need to be plenty stubborn and amazingly bored to get a proper grip of the system without a guiding voice to tell you what does what, but thankfully I have both stubborness and boredom in spades, and after finally discovering Epic Developer Dave Ratti's example github project(which goes through some basic examples to get you started, but ignores the fine lines and goes for broad strokes, for now) and working out the specifics myself for a few weeks, I think I am at last confident enough to write a little guide on how to get started with this behemoth of a module, so noone has to go through the same painstaking process I had to endure. Moreover, now that GameplayTags are properly integrated into the editor by default(a system GameplayAbilities itself uses at every corner of the way, acting as GameplayAbilities' backbone), setup has never been any easier!



    With all that said, let's get started, finally.

    Getting started + Initial setup for your Ability System Component

    So, first of all, let us create an all-new c++ third person project, not just because I want you to properly understand the specifics of enabling the system for your own use, but also because I want to start on a clean slate so that you may not be confused by assets which you do not have on hand.
    Click image for larger version  Name:	Unbenannt.PNG Views:	1 Size:	247.8 KB ID:	1211616

    This should be a fairly straightforward and obvious step to anyone that has ever created a UE4 c++ project before. I'm calling it GameplayAbilitiesTut, but you may call it as you'd like, really, as long as you pay attention and replace my project's name with yours while coding and understanding.
    Alright, we're here. Good old third person template, such a familiar environment, and so useful for tutorials! We want to open the plugin menu, accessed through the Settings tab.

    Click image for larger version  Name:	Unbenannt1.PNG Views:	1 Size:	401.2 KB ID:	1211617

    We find GameplayAbilities in the Gameplay category. Enable it. Do not be scared off by the big scary "[UNSUPPORTED]" in the description or the prompt that asks you if you're sure. You know darn well you're sure!
    You must now restart the editor to fully enable the plugin. It contains a few menus and a new blueprint type to select from the new asset-menu, but it won't load those until the next restart.

    After you restart, you may or may not notice a few new things: A new blueprint type called "Gameplay Ability Blueprint" when you press rightclick in the content browser to create a new blueprint and a new window in the window menu called "GameplayCue Editor".
    Click image for larger version  Name:	Unbenannt5.PNG Views:	1 Size:	441.6 KB ID:	1211619

    Click image for larger version  Name:	Unbenannt4.PNG Views:	1 Size:	382.3 KB ID:	1211618

    We don't go into specifics with these just yet, but we do want to create a Gameplay Ability Blueprint, mostly because it's pretty much just a generic blueprint for abilities, and we will need one to test our AbilitySystemComponent later. Select "GameplayAbility" as your blueprint's parent, open the blueprint and just link a Print String node to the ActivateAbility event. Now you know when your AbilitySystem successfully calls your ability, because then a reassuring light-blue "Hello." will show on the screen. Self-explanatory, really.

    Alright, I hope you got your Visual Studio ready already, it's time for some nitty gritty code. We want to give our character an ability component to use.

    ... well, not quite, anyway. We need to tell our compiler that we want to use the GameplayAbilities module first. Go into your project's Build.cs file(in my case it's GameplayAbilitiesTut.Build.cs) and change this

    Code:
    PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay" });
    to this

    Code:
    PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "GameplayAbilities" });
    Basically, we add "GameplayAbilities" to the list. Don't worry about getting it wrong, your compiler will immediately start nagging if it can't find the module with the name you typed in.
    Adding the module name into this list assures that the module will be properly linked to our project. Without it our compiler would throw out a bunch of confusing external linker errors each time we were to include a header from this module into our project's files.
    Now, open your project's c++ character. This will be GameplayAbilitiesTutCharacter for me. Go into the class header and declare a new pointer to a UAbilitySystemComponent right below your other component pointers. You should also give it a UPROPERTY macro. It's okay to copy and paste the UPROPERTY from your camera components, but you should probably change the category to something like "Abilities" for clarity reasons. It should look a little like this.

    Code:
    /** Camera boom positioning the camera behind the character */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
    class USpringArmComponent* CameraBoom;
    
    /** Follow camera */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
    class UCameraComponent* FollowCamera;
    
    /** Our ability system */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Abilities, meta = (AllowPrivateAccess = "true"))
    class UAbilitySystemComponent* AbilitySystem;
    What is also extremely important, so we don't get into trouble later down the line, is to make it so that our character implements the IAbilitySystemInterface. The guide assumes basic programming knowledge, you should know about what an interface does, so I won't get too much into detail, but it allows us to define a pseudo-parent of sorts that defines functions we have to override. This interface here gives other actors an easy way to both know we have an ability system, and a way to get it without doing something dumb and inefficient like iterating through our components for an ability system. Many features will not run properly without this interface implemented. Our code until now would run fine without it, but you will head into trouble once we're done with our initial setup and want to throw buffs and similar things on our character.

    Code:
    #include "AbilitySystemInterface.h" //We add this include
    
    UCLASS(config=Game)
    class AGameplayAbilitiesTutCharacter : public ACharacter, public IAbilitySystemInterface //We add this parent.
    {
    
    UAbilitySystemComponent* GetAbilitySystemComponent() const override //We add this function, overriding it from IAbilitySystemInterface.
    {
    return AbilitySystem;
    };
    Further, we go into our cpp file and go to the constructor of your character. For me that is GameplayAbilitiesTutCharacter.cpp. We need to actually create the component, and have our pointer point to it. As you will actually create an object of type UAbilitySystemComponent now, you must include "AbilitySystemComponent.h" in your cpp file. Top of the file up to constructor should look a little like this now.

    Code:
    // Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
    
    #include "GameplayAbilitiesTut.h"
    #include "Kismet/HeadMountedDisplayFunctionLibrary.h"
    #include "GameplayAbilitiesTutCharacter.h"
    #include "AbilitySystemComponent.h"
    
    //////////////////////////////////////////////////////////////////////////
    // AGameplayAbilitiesTutCharacter
    
    AGameplayAbilitiesTutCharacter::AGameplayAbilitiesTutCharacter()
    {
    // Set size for collision capsule
    GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
    
    // set our turn rates for input
    BaseTurnRate = 45.f;
    BaseLookUpRate = 45.f;
    
    // 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, 540.0f, 0.0f); // ...at this rotation rate
    GetCharacterMovement()->JumpZVelocity = 600.f;
    GetCharacterMovement()->AirControl = 0.2f;
    
    // Create a camera boom (pulls in towards the player if there is a collision)
    CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
    CameraBoom->SetupAttachment(RootComponent);
    CameraBoom->TargetArmLength = 300.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("FollowCamera"));
    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
    
    // Our ability system component.
    AbilitySystem = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("AbilitySystem"));
    
    // Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character)
    // are set in the derived blueprint asset named MyCharacter (to avoid direct content references in C++)
    }
    You may try to compile if you are unsure whether you did everything the right way.

    Once you have compiled, you can open your character blueprint(which inherits from your c++ character) and lo and behold, right under the character's movement component you should see an AbilitySystemComponent.

    Click image for larger version  Name:	Unbenannt6.PNG Views:	1 Size:	384.1 KB ID:	1211620

    Alright, well... what now? The blueprint menu for the component is not helpful at all, and none of the nodes you get by dragging off AbilitySystem are particularly useful, either. There's these "Try Activate Ability nodes", but you may find out that these things don't do anything right now.
    That's because the ability system doesn't have any abilities to activate yet, nor does it have any inputs assigned to them, anyway, so trying to activate an ability you do not have is, obviously, a quite useless effort. We will work on fixing both things. You must do both things in c++.

    First of all, we will bind our ability system to our character's input, because it's the slightly more complicated issue and it's actually pretty interesting on how you do it.
    So first of all, go back to your character's cpp file, and go to the SetupPlayerInputComponent function. It's the one responsible for binding your character's inputs to the player controlling it, and takes a UInputComponent as parameter. This is important, we need it to bind our ability system to it. We want to call AbilitySystem->BindAbilityActivationToInputComponent within the SetupPlayerInputComponent. It takes two parameters: The UInputComponent pointer at hand and a struct called FGameplayAbiliyInputBinds. This is not a typo! It is not called FGameplayAbilityInputBinds, but FGameplayAbiliyInputBinds!

    Yeah, so, FGameplayAbiliyInputBinds, the constructor takes at least 3 parameters. The first two are strings, and represent the input names that will be used to define "Confirm" and "Cancel"-input commands. You do not necessarily need these depending on your game, but abilities can be set up to listen to these while they're active, and targetting actors(basically, actors that return an ability viable targets/locations to aim at for an ability, if an ability requests one) will use these too, so generally it can't hurt to have these even if you will never use them. The third parameter is the name of an arbritrary UEnum of all things. This is one of the witchcraft-ier aspects of the system: The ability system component will look into the enum whose name you've given and will map its ability slots to the names of the elements contained within the enum. This probably sounds way complicated from the way I'm describing this, but it's actually quite simple. This is an input enum lifted from my own project:

    Code:
    //Example for an enum the FGameplayAbiliyInputBinds may use to map input to ability slots.
    
    //It's very important that this enum is UENUM, because the code will look for UENUM by the given name and crash if the UENUM can't be found. BlueprintType is there so we can use these in blueprints, too. Just in case. Can be neat to define ability packages.
    UENUM(BlueprintType)
    enum class AbilityInput : uint8
    {
    UseAbility1 UMETA(DisplayName = "Use Spell 1"), //This maps the first ability(input ID should be 0 in int) to the action mapping(which you define in the project settings) by the name of "UseAbility1". "Use Spell 1" is the blueprint name of the element.
    UseAbility2 UMETA(DisplayName = "Use Spell 2"), //Maps ability 2(input ID 1) to action mapping UseAbility2. "Use Spell 2" is mostly used for when the enum is a blueprint variable.
    UseAbility3 UMETA(DisplayName = "Use Spell 3"),
    UseAbility4 UMETA(DisplayName = "Use Spell 4"),
    WeaponAbility UMETA(DisplayName = "Use Weapon"), //This finally maps the fifth ability(here designated to be your weaponability, or auto-attack, or whatever) to action mapping "WeaponAbility".
    
            //You may also do something like define an enum element name that is not actually mapped to an input, for example if you have a passive ability that isn't supposed to have an input. This isn't usually necessary though as you usually grant abilities via input ID,
            //which can be negative while enums cannot. In fact, a constant called "INDEX_NONE" exists for the exact purpose of rendering an input as unavailable, and it's simply defined as -1.
            //Because abilities are granted by input ID, which is an int, you may use enum elements to describe the ID anyway however, because enums are fancily dressed up ints.
    };
    Basically, this means we need to define an enum, too. Let's just do it in our GameplayAbilitiesTutCharacter's header. You may copypaste this enum here if you wish, I will use it like that, even if 5 slots may be a little overkill for the purpose of example.
    Finally, our function should look something like this:

    Code:
    AbilitySystem->BindAbilityActivationToInputComponent(PlayerInputComponent, FGameplayAbiliyInputBinds("ConfirmInput", "CancelInput", "AbilityInput"));
    Place this code at the end of your SetupPlayerInputComponent function, and you should be gravy. You have successfully bound your ability system's ability activation to player input!



    The final step of our setup is to finally give the character an ability of choice. For simplicity's sake we will only give him one on the action mapping "UseAbility1" and just give the actor a variable that defines which ability to put there, but the same principles for granting one ability are applicable for multiple ones. We will make it blueprint-edittable too so we can easily change the ability we want to test later down the line.

    Our variable will be a TSubclassOf<UGameplayAbility>, because we get all relevant info from the class alone. In fact, GameplayAbilities can be set up to only instance per activation or not to instance at all even, so giving an instance we can freely change beforehand would be a weird idea, anyway.
    Code:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Abilities)
    TSubclassOf<class UGameplayAbility> Ability;
    In BeginPlay, we will call AbilitySystem's GiveAbility function. We actually wrap this in an if-statement that first checks if we are authority. If a client tries to give himself an ability, an assert is violated and the game goes to crash and burn, taking the editor with it. You've been warned. Only give abilities on the server... or else!
    We'll also need to check if Ability is valid, and not NULL/nullptr.
    GiveAbility requests an FGameplayAbilitySpec as parameters. An FGameplayAbilitySpec is the daa surrounding a GameplayAbility, notably which level(the system has built-in support for a level variable, quite good for RPGs/MOBAs as mentioned) and which input ID it is.
    FGameplayAbilitySpec requests a GameplayAbility object as parameter, but that's not a problem; we can just give the Ability class' default object as parameter. There is very little reason to use anything other than the default object of a GameplayAbility class as far as I've understood it from going through the source.
    Finally, while on the topic of BeginPlay, we should also call AbilitySystem->InitActorInfo. It tells the AbilitySystem what its owner(the actor responsible for the AbilitySystem) and Avatar(the actor through which the AbilitySystem acts, uses Abilities from etc.) is. In our case our character is both.
    Our final BeginPlay should look something like this:

    Code:
    void AGameplayAbilitiesTutCharacter::BeginPlay()
    {
       Super::BeginPlay();
       if(AbilitySystem)
       {
          if (HasAuthority() && Ability)
          {
     AbilitySystem->GiveAbility(FGameplayAbilitySpec(Ability.GetDefaultObject(), 1, 0));
          }
          AbilitySystem->InitAbilityActorInfo(this, this);
       }
    }
    EDIT: You also need to make sure that the AbilitySystemComponent's ActorInfo struct is being updated each time the controller changes. On the surface much of the system will work without that, but in a multiplayer enviroment especially(where pawns may be spawned before the client controller possesses them) you will experience crashes and behaviour that can be difficult to debug should you not properly set the ActorInfo up. Override your character's/pawn's OnPossessed function like so:

    Code:
    void AGameplayAbilitiesTutCharacter::PossessedBy(AController * NewController)
    {
        Super::PossessedBy(NewController);
    
        AbilitySystem->RefreshAbilityActorInfo();
    }
    Compile, add an action input mapping in your project settings called "UseAbility1" and start the game. If the game doesn't crash and the mapped input produces a plain old "Hello.", then congratulations! You have successfully set up your gameplay ability system for this character. That was the worst and dryest part, so you are allowed to be proud of yourself! We can finally move on to the actually exciting part of using the system.

    The Essentials: A crash course on GameplayAbilities, GameplayAttributes and GameplayEffects

    Alright, you should now have at least one functional GameplayAbility bound to your character, which is pretty cool. However, we haven't even gotten into what GameplayAbilities can do yet. Heck, we haven't gotten to what anything at all does yet, because setup took so long. However, GameplayAbilities are a great place to start general comprehension.

    GameplayAbilities

    GameplayAbilities are the stupidly flexible implementation of spells, skills and such in this system. Not only do they have easy support for such things as cooldown, costs and other common RPG-ish stock spell features, but they are set up in such a way that you may call so-called Ability Tasks within them. These are specialized asynchrous tasks that you may request to run during an ability's active period, returning to the blueprint graph once the task has completed its... well, task, or until a certain event or passage of time prompts the task to call back. They're in a sense very much like your run-of-the-mill Blueprint Delay node, but they can do oh-so-much-more than just waiting a certain amount of time to continue. They may, for instance, wait for an input, or for a collision/overlap, or for a montage to finish playing(going to a different execution path when ending normally or when interrupted), they can even wait for client and server to sync up to a certain point. This means that an ability must not immediately be done after the initial activation frame, but may consist of one to several different time-consuming processes before finally being finished. Wait for an animation to finish playing before firing a fireball? Easy. Charge the fireball by holding down the button mapped to the ability, releasing the button to fire the fireball? Easy. Heck, you could probably program an ability that forces you to play DDR with your fingers before shooting a fireball, with the fireball getting stronger with godlike finger dancing skills, if you really wanted to.
    This comes at a small price though, because an ability activation always needs to directly or indirectly call EndAbility to announce that its Activation has ended. By default you will be unable to trigger an activation past the first one otherwise(though there is an option to be able to reset a running ability when pressing the activate-button), and it will be considered permanently active for all intents and purposes, which may mess with other abilities or aspects of the system. You must also manually call "Commit Ability" within the ability activation, which checks for and applies the likes of cost and cooldowns.
    An ability is also able to control its own instancing state, and each ability may independently choose whether they do not want to be instanced(no ability tasks, no personal state and variables and some other limited functionality, but ridiculously cheap so preferable if you can get away with it), instance on activation(personal state limited to a per-activation basis, variables and such can be replicated but it is not recommended) or instanced per ability owner(most expensive, but variables can easily be replicated, state can be carried across activations(for example, a fireball that gets stronger with each use would be possible without permanently considering the ability active) and most functions are intact).
    Finally, abilities can be useful for certain passive effects too, as abilities can listen for tags being granted upon their owners or Gameplay Events firing in the owning Ability System Component(more on that another time). Buffs that respond to certain outside influences may implement themselves by granting the affected actor with a hidden passive ability to listen for these, for example.
    As such, GameplayAbilities are extremely useful, and you'd do good to learn how to best make use of them.

    GameplayAbilities possess the following notable Class Default variables:
    • Ability Tags: Gameplay Tags the ability uses as flags, so to speak. Gameplay Tags are pretty much a global list of names and terms that can be used by assets as generic names and labels. In the context of GameplayAbilities these can be useful by having a GameplayAbility use an Ability Task to listen for the activation of a different ability with specified tag as ability tag. Alternatively, an ability may cancel other currently active abilities that are described to have ability tag x, or it may be blocked from activating while ability with ability tag x is active. These are easy ways to set up global behaviour and interaction between different abilities. Perhaps only one transformation active can be active at a time? Perhaps activating fire magic while water magic is active cancels one or both? It's up to you and what type of game you want to make. There are no strict rules.
    • Cancel Ability with Tags: Abilities with these tags will be cancelled upon activation of this ability.
    • Block Ability with Tags: Abilities with these tags will be blocked while this ability here is active.
    • Activation Owned Tags: The owner of the ability gets these tags while the ability is active. This is something different than ability tags, because the owner gets these here. GameplayEffects(buffs) may interact with them this way, and other abilities can, as already mentioned, listen for a tag to be granted to its owner to active. This has a lot of uses if you get creative with it, you could make the user of the ability immune to damage while they are casting this, etc.
    • Activation Required Tags: The owner ahs to have these tags BEFOREHAND so that it may become activatable. Great for, say, buffs that allow you access to strong abilities, or perhaps status effect-purging abilities that are only activateable as you are affected by the status effect at hand.
    • Activation Blocked Tags: Same but in reverse, the user of the ability must not have these tags. Excellent for crowd control effects such as silences, stuns, roots(which, in some games, disable movement-related abilities), you name 'em.
    • Source Required Tags: The source must have these tags. This is usually the ability's user, but it can be someone else, too. Maybe you have a debuff that grants a passive ability that slowly damages allies around the affected actor? That'd be an example.
    • Source Blocked Tags: Same but with blocked tags.
    • Target Required Tags: Frankly I never used this, but you usually inflict buffs, debuffs and the like in GameplayAbilities by passing AbilityTargetDatas around. You can create these from locations, actors, trace hit results, all sorts of things really. My best guess is that when a Target Data points to an actor without any of these tags, the ability will cancel/not fire after that point. I'd need to try this out myself, actually.
    • Target Blocked Tags: Same but with Blocked tags.
    • Cost GameplayEffect: This is a GameplayEffect(in a sense a buff, or instant stat modifying action) that may contain instant, and thus permanent, stat modifiers, for example for mana and stamina and such. This checks if the attribute in question will be lowered below 0 by one such instant modifier. If so, Commit Ability will prompt the ability to end prematurely.
    • Ability Triggers: Can be used for remote ability activation. You can choose to activate the ability in response to a tag being granted to the owner, the tag being present on the owner(ending the ability automatically when the tag ceases applying(?)), or a Gameplay Event labelled with the specified tag being handled by the ability's owning Ability System Component.
    • Cooldown GameplayEffect: This GameplayEffect represents the ability's cooldown. When checking for cooldown, the ability will look into this gameplay effect's granted tag container(so the tags it may grant to the owner while active) and will then check if the using ability system has any of these tags granted to them. If yes, the ability will be considered on cooldown. This essentially means that all independent cooldowns need their own dedicated gameplay tag, but it also means that multiple abilities can easily share a cooldown, outside events may easily set a particular cooldown and one ability may also have a gameplay effect sharing cooldowns with 2 different kinds of cooldowns that do not influence each other. A cooldown gameplay effect can also be set to 0/really low so that you may set the cooldown manually with the tags specified in the dedicated cooldown gameplayeffect as the ability is running, which can be useful if your ability doesn't always have a predictable and predefined cooldown in mind.






    GameplayEffects

    GameplayEffects can be described as this system's dedicated buff class. Their functionality goes a little beyond that, and in fact most stat modifiers regardless of instant and permanent or over time and temporary are usually GameplayEffects. They're very peculiar in how they are set up to work as they are built to be hyperefficient to replicate/network in general and as such GameplayEffects are, first and foremost, glorified struct-like data assets with in-blueprint inheritance and without the ability to change their variables during runtime, as they will often be passed by class reference alone. In fact, you will almost NEVER see a plain GameplayEffect being passed around in code and especially not in Blueprint. The tooltip says they're data-only, and they really do mean that. GameplayEffects usually use GameplayEffectSpecs to move around, which are huge behemoths of structs that store everything from effect context(what level, who is instigator, who is target, which ability spawned me, why do I even exist?) to reference to the GameplayEffect class that defines most default behaviour and variables to stack count to potential extra modifiers/tags/whatever to pass in alongside what the class reference defines upon applying. In a strange sort of way, GameplayEffectSpecs are much closer to object instances than the GameplayEffect instances themselves are. As such, if you want to apply a gameplay effect via ability, either apply it directly through a class reference or create a gameplay effect spec within the ability and use that to apply a gameplay effect.
    Anyhow, due to the unusual nature of Gameplay Effect blueprint classes, most of their variables are either simple values, other direct class references or just tags. There are too many to list and after explaining how abilities and their tags work, it should be fairly self-explanatory what most tags are used for, or do. It should however be noted that Gameplay Effects have 3 containers for each type of tag, one that is not directly edittable, one that describes tags added on top of tags potentially owned from a parent and tags that are removed from a potential parent. Basically, this tag inheritance setup is one of relatively few reasons why Gameplay Effects are full-fledged UObject classes in the first place.
    Some of the more notable variables are:
    • Duration Policy: Is the effect instant, does it have a fixed duration, or does it go on infinitely? Do note that instant effects turn modifiers into permanent stat changes, and executions will be triggered immediately.
    • Modifiers: Stat changes in all shapes and forms. Whether you want to add a flat amount to a stat, multiply a stat, divide, override with a fixed value or do any of these things in relation to other stats.
    • Executions: Executions are an interesting case: They are essentially the functions the gameplay effect itself can't have(due to being meant to be as data-only as possible). An Execution takes a GameplayEffectExecutionCalculation as parameter, a class that is set up to define attributes to capture from both target and source, and to do things with them that would be considered too complex with modifiers alone. They are more or less meant to do as they please, however they cannot listen to events and such like abilities can do and pretty much only run in fixed, predefined intervals on timed gameplayeffects(and optionally once on application), or immediately on application in the case of instant gameplayeffects. They're your go-to for complex damage calculation and the likes. More on that later.
    • Stacking: You know how in some games certain buffs/debuffs of one kind can stack on a target? This behaviour is managed here. By default all gameplayeffects of same type will act and tick down independently(though requesting the amount of stacks of a gameplay effect will usually still show the total amount of effect instances of this type). There are options to make them all go on the same timer, removing one stack each time duration runs out, removing all of them once the timer runs out once, if application of a new stack refreshes the current duration or if there is a cap on stacks. You can get quite creative with these.
    • Overflow: Adding up on stacks, overflow effects are essentially effects that the affected actor will be affected by when the max amount of stacks of this gameplay event has been reached. If you get cold enoguh you freeze, breath enough poison gas to get heavily poisoned, whatever, you get it.
    • Display: You can define GameplayCues to use here. At their most basic, GameplayCues are essentially visual/audible effects that respond to a specialized tag(It needs to have "GameplayCue" as parents, so for example "GameplayCue.DoT.Fire") they've been assigned to. You can call these directly in abilities too. They're a network-friendly way to spawn stuff like particle effects, cosmetic meshes and sound effects to provide your debuffs and skills with some eye candy. How they react to being called by the GameplayEffect/Ability is defined within the GameplayCue itself(there's 4 types of events a GameplayCue will respond to: On Applied(activates when applied), While Active(constantly triggers while active, presumably), Removed(when... well, removed) and Executed(this is interesting: When a gameplayeffect's execute classes run, this will play).
    • GrantedAbilities: This has many uses. You may use a buff to temporarily provide an active ability as part of the buff(maybe a fire mage can give someone else a fire ability by igniting one of his allies? Heh, gotta love combat arson), but, more importantly, you can use these for effects that are too specific for modifiers but need to be permanently active in a way effect executions can't. If a gameplay effect is tagged to grant an actor an OnFire tag, you may have an ice buff with a passive ice ability granted listen for this event and remove the offending effect, as well as the ice buff itself(GameplayAbilities have a function just to allow them to remove the effect that granted them). Together with modifiers and executions, this allows you to do virtually anything with your effects.






    EDIT: It should be noted that most float values put into are not actually just plain float values, but rather a struct called FScalableFloat. You can use it just like any regular float, but there is an asset pointer to the right of the box where you'd put the float value in. It may confuse you because there are no valid references to use, and there is no option to create a new one. This slot is reserved for a Curve Table, an asset you get by importing a csv, or file with a comparable table file format, into the project. This is one of the few things where effect level makes a difference, as the table will then look at the column labelled with this level(or the columns it should be between, determining the value dependent on what kind of graph the table row is set to describe) so if you use levels in, for example, your GameplayEffectExecutionCalculations, keep that in mind, as you may accidentally set a value to scale in unexpected ways otherwise.

    AttributeSet

    AttributeSets are thankfully very simple to explain. They define FGameplayAttributeData structs(structs containing float attributes to represent the flat base value of an attribute, as well as the attribute value after modifiers have been applied to it) and can be connected to AbilitySystems to grant the ability system in question these attributes. GameplayEffects and GameplayEffectExecutionCalculations have specifically designed macros and menus to manipulate these attributes on an ability system. An ability system may use multiple attribute sets or none at all, too. The system accounts for attributes it cannot find and will simply ignore stats that are not appropriate for the particular actor and his AbilitySystem. As such, maybe both players and foes have Health, Mana, attack damage, defense, you name 'em, and players then have an extra attribute set containing RPG attributes such as Strength, Intelligence, Constitution and the like. These are all perfectly possible scenarios, and it's nice that the system gives you the option to mix and match multiple attribute sets.
    The best way to bind an attribute set to an ability system is to create the AttributeSet as the same actor's subobject in the constructor. The ability system should find it by itself. It does for me, at least.

    Attributes within attribute sets are defined like any other UPROPERTY, which is amazingly practical and straightforward. Why can't everything in this module be... Well it isn't that easy anyway, due to the AttributeSet's functions, which either deal with finding out which UPROPERTY the current parameter is talking about or have to do with the infinitely more complex GameplayEffectExecutionCalculation.
    PreAttributeBaseChange is called before... well, an attribute's base value(so without any temporary modifiers) is changed. It would be unwise to use this for game logic, and is mostly there to allow you to describe stat clamping. PreAttributeChange is in the same boat, but here you can define clamping with temporary modifiers instead. Either way, NewValue describes the new value of a changed stat, and FGameplayAttribute Attribute describes some info about the stat we're talking about.
    If you want to find out if this particular Attribute change is talking about particular Attribute MyAttribute in UMyAttributeSet, you'd do it something like this:

    Code:
    Attribute.GetUProperty() == FindFieldChecked<UProperty>(UMyAttributeSet::StaticClass(), GET_MEMBER_NAME_CHECKED(UMyAttributeSet, MyAttribute))
    This code takes the UProperty variable of the Attribute parameter and checks if the referenced uproperty is identical with the one that describes MyAttribute in UMyAttributeSet. The macro is mostly there for safety, I believe this is actually defined as a relatively simple string.

    PreGameplayEffectExecute is a function that takes the data a GameplayEffectExecutionCalculation spits out(including which stats it wishes to modify, and by how much), and can then decide if the GameplayEffectExecutionCalculation is allowed to influence the AttributeSet in any way, by returning an appropriate bool. PostGameplayEffectExecute happens after this evaluation and as such you are unable to throw the GameplayEffectExecution out properly by then. However, because 90% of the time things such as damage calculations will be effect executions, here will be an excellent place to wrap such a thing up, such as by, for example, checking if the damage you took killed you.

    EDIT: Replication info update. Replication of attributes should now work properly.

    In the case of a multiplayer game, attributes must usually still be replicated. You replicate them like any other c+ variable, by including the UnrealNetwork.h in your header, adding a "Replicated" tag inside the variable UPROPERTY macro and overriding void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const so that the variable is properly included as replicated variable. However, the system requires some extra replication parameters that the normal DOREPLIFETIME macro does not set properly. As such, we need to use a macro which has more parameters, and set these accordingly. It's thankfully quite simple, as all attributes will use the same settings.

    Code:
    void UWizardAttributeSet::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
    {
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    
    //DOREPLIFETIME( UMyAttributeSet, MyAttribute); Chances are this is how you would ordinarily do it, however in the case of attributes this'll lead to confusing and annoying replication errors, usually involving clientside ability prediction.
    DOREPLIFETIME_CONDITION_NOTIFY( UMyAttributeSet, MyAttribute, COND_None, REPNOTIFY_Always); //This is how it is done properly for attributes.
    }
    However, attributes need some extra legwork so that values and structs depending on this attribute in question get changed according to a value a client receives from the server. We need to replace the "Replicated"-tag in your UPROPERTY with a "ReplicatedUsing=OnRep_MyFunction" tag, with OnRep_MyFunction being the function you wish to call to update your current attribute. Functionally this means each attribute needs its own OnRep function, like so:

    Code:
    UPROPERTY(Category = "Attribute", EditAnywhere, ReplicatedUsing = OnRep_MyAttribute, BlueprintReadWrite)
    FGameplayAttributeData MyAttribute;
    UFUNCTION()
    void OnRep_MyAttribute()
    {
    GAMEPLAYATTRIBUTE_REPNOTIFY(UMyAttributeSet, MyAttribute);
    }
    That's a lot to write for one attribute, so if you are sure you do not need the OnRep function for anything else, you may set up a macro that does this whole thing for you, as most of these functions will look about identical to each other.

    EDIT: Originally, attributes set up in this attribute set were plain float variables, which through some UProperty magic essentially got turned into proper GameplayAttributes automatically. The system still does that, however defining float attributes is considered deprecated, and you should define them as FGameplayAttributeData instead(which essentially does the same thing plain float attributes do, presumably cleaner and more "future-proof", so to speak). FGameplayAttributeData is a struct containing 2 floats: One holding the base value of an attribute, and one holding its value with boosts, buffs and similar included. I have changed the example code here to reflect that, however there may be some possible inconsistencies arising in this guide as a result.

    This should be the basics. With knowledge of GameplayAbilities, GameplayEffects, AttributeSets and a tiny crash course on GameplayCues, you should be able to get simple types of abilities, buffs and debuffs done. You may explore for yourself and see what the system allows you to accomplish. You will probably find it to be very powerful and simple to use once you've gotten into its rhythm.

    The More Advanced Nitty Gritty: GameplayEffectExecutionCalculation, GameplayEvents, and the rest.

    So we have Abilities, Attributes and Effects now. Cool. However, with the tools we have currently introduced, it is difficult to really tie the individual components of this system into each other: Abilities can be called remotely, but only when tags are/have been granted to their owner and without any parameters to work with, GameplayEffects are severely limited by modifiers being so basic and abilities requiring explicit outside triggers to really do anything, and attributes... well, those are actually working just fine considering they're just float containers at heart, but accessing them and setting up global calculations with them could be easier.
    Anyhow, GameplayEvents and GameplayEffectExecutionCalculations(which I will call EffectExecs from now on, because that word is like 35 letters long) are there to really tie up the loose ends of the system together and really make a proper package out of the single excellent systems we have right now.

    GameplayEffectExectutionCalculation

    To put it simply, a GameplayEffectExecutionCalculation is a function a GameplayEffect may have and may call in fixed intervals over the effect's duration and/or during initial application. They can do whatever they want really as their Execute-function provides them with all parameters necessary to influence their respective actor, ability system or even outside world directly, but due to being a little inconvenient to set up, being C++ only for the moment and lacking any real way to react to the outside world in the way abilities can, you may be better off with abilities instead depending on what you want to do.
    However, an EffectExec's unique gimmick is that it can capture attributes from both Source of the GameplayEffect and Target of the GameplayEffect while applying a modifier to them just for this function activation, and use them as parameters of sorts for the calculation, being also able to snapshot particular attributes when the GameplayEffect is first conceived if such a thing would be necessary(for instance, you can attach a GameplayeffectSpec to a fireball projectile, applying it to whoever gets hit, and the fireball naturally shouldn't be influenced by damage boosts and changes on the source once it has initially been fired). This makes EffectExecs amazingly useful for things such as global damage calculations, which will also be our go-to example to understand the setup with in this guide. It will be a very simple and naively implemented example, but it will help you set up a more complex one.

    For starters, assume that we have an arbritrary attribute system possessing the following attributes: Health, AttackMultiplier and DefenseMultiplier. Health will decrease as damage is taken(I mean, obviously), AttackMultiplier multiplies outgoing damage with itself and DefenseMultiplier will multiply incoming damage with itself(usually being below 1, or 100%, essentially reducing incoming damage). I will assume that you will have experimented with the system in the previous section of the Essentials already and can just add these to your other attributes if you do not already possess similar ones(I'd be surprised if you wouldn't have a Health attribute by now, that's usually the first thing everyone does), but just in case, the code of an attribute system with just these values could look a little like this:

    Code:
    /**
     *
     */
    UCLASS()
    class UMyAttributeSet : public UAttributeSet
    {
    GENERATED_BODY()
    
    public:
    
    //Hitpoints. Self-explanatory.
    UPROPERTY(Category = "Wizard Attributes | Health", EditAnywhere, BlueprintReadWrite)
    FGameplayAttributeData Health;
    
    //Outgoing damage-multiplier.
    UPROPERTY(Category = "Wizard Attributes | Health", EditAnywhere, BlueprintReadWrite, meta = (HideFromModifiers))
    FGameplayAttributeData AttackMultiplier;
    
    //Incoming damage-multiplier.
    UPROPERTY(Category = "Wizard Attributes | Health", EditAnywhere, BlueprintReadWrite)
    FGameplayAttributeData DefenseMultiplier;
    
    }
    However, we actually want to add another attribute on top of that. Because an EffectExec takes attributes as pseudo-parameter, we want an extra attribute just so we may define an effect's base damage. We could combine AttackMultiplier and BaseAttackPower into one attribute, but you may get into deep feces once you want to add buffs that influence your outgoing damage, and simply adding values to your BaseAttack may have quite notable balance implications and such if you have a rapidfire ability that deals a lot of very small damage effects. You COULD change BaseAttack for some buffs and effects, but that's mostly you and your game's call. Basically, having a percentage multiplier on top of a flat attack value is probably a better idea. Anyhow, you should add BaseAttackPower as an attribute.

    Code:
    /**
     *
     */
    UCLASS()
    class UMyAttributeSet : public UAttributeSet
    {
    GENERATED_BODY()
    
    public:
    
    //Hitpoints. Self-explanatory.
    UPROPERTY(Category = "Wizard Attributes | Health", EditAnywhere, BlueprintReadWrite)
    FGameplayAttributeData Health;
    
    //Outgoing damage-multiplier.
    UPROPERTY(Category = "Wizard Attributes | Health", EditAnywhere, BlueprintReadWrite, meta = (HideFromModifiers))
    FGameplayAttributeData AttackMultiplier;
    
    //Incoming damage-multiplier.
    UPROPERTY(Category = "Wizard Attributes | Health", EditAnywhere, BlueprintReadWrite)
    FGameplayAttributeData DefenseMultiplier;
    
    //Base damage of an outgoing attack.
    UPROPERTY(Category = "Wizard Attributes | Health", EditAnywhere, BlueprintReadWrite)
    FGameplayAttributeData BaseAttackPower;
    }
    Alright, so we got all our important attributes set up, it's time to create a new EffectExec. Go to your C++ folder in your content explorer, click New C++ class, select GameplayEffectExecutionCalculation as your parent, and select a name for your new class that doesn't take half a decade to pronounce or type. I am calling mine DamageExec. You may do too, if you like.
    Once it has finished compiling, you want to change GENERATED_BODY() at the top of your class delcaration in your header to GENERATED_UCLASS_BODY(). This way, Unreal's preprocessor-generation-thingie will define us a constructor DamageExec(const FObjectInitializer& ObjectInitializer). We want to implement it in our cpp file like so.

    Code:
    UDamageExecution::UDamageExecution(const FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
    {
    
    }
    We will actually need to do a few things in our constructor, namely giving the Execution info on what attributes we wish to capture from whom. We will need FGameplayEffectAttributeCaptureDefinitions for this. Thankfully, the module has macros for these that makes it easy to set them up.
    For the sake of simplicity(as we will need the definitions and UProperties of our attributes in different functions), we will put these in a struct.

    Code:
    struct AttStruct
    {
    DECLARE_ATTRIBUTE_CAPTUREDEF(Health); //The DECLARE_ATTRIBUTE_CAPTUREDEF macro actually only delacres two variables. The variable names are dependent on the input, however. Here they will be HealthProperty(which is a UProperty pointer)
                                                                             //and HealthDef(which is a FGameplayEffectAttributeCaptureDefinition).
    DECLARE_ATTRIBUTE_CAPTUREDEF(AttackMultiplier); //Here AttackMultiplierProperty and AttackMultiplierDef. I hope you get the drill.
    DECLARE_ATTRIBUTE_CAPTUREDEF(DefenseMultiplier);
    DECLARE_ATTRIBUTE_CAPTUREDEF(BaseAttackPower);
    
    AttStruct()
    {
    // We define the values of the variables we declared now. In this example, HealthProperty will point to the Health attribute in the UMyAttributeSet on the receiving target of this execution. The last parameter is a bool, and determines if we snapshot the attribute's value at the time of definition.
    DEFINE_ATTRIBUTE_CAPTUREDEF(UMyAttributeSet, Health, Target, false);
    
    //This here is a different example: We still take the attribute from UMyAttributeSet, but this time it is BaseAttackPower, and we look at the effect's source for it. We also want to snapshot is because the effect's strength should be determined during its initial creation. A projectile wouldn't change
                    //damage values depending on the source's stat changes halfway through flight, after all.
    DEFINE_ATTRIBUTE_CAPTUREDEF(UMyAttributeSet, BaseAttackPower, Source, true);
    
    //The same rules apply for the multiplier attributes too.
    DEFINE_ATTRIBUTE_CAPTUREDEF(UMyAttributeSet, AttackMultiplier, Source, true);
    DEFINE_ATTRIBUTE_CAPTUREDEF(UMyAttributeSet, DefenseMultiplier, Target, false);
    }
    };
    Now we have a struct that contains the CaptureDefinitions we need, so in the constructor we can simply write:

    Code:
    UDamageExec::UDamageExec(const FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
    {
          AttStruct Attributes;
    
          RelevantAttributesToCapture.Add(Attributes.HealthDef); //RelevantAttributesToCapture is the array that contains all attributes you wish to capture, without exceptions.
          InvalidScopedModifierAttributes.Add(Attributes.HealthDef); //However, an attribute added here on top of being added in RelevantAttributesToCapture will still be captured, but will not be shown for potential in-function modifiers in the GameplayEffect blueprint, more on that later.
    
          RelevantAttributesToCapture.Add(Attributes.BaseAttackPowerDef);
          RelevantAttributesToCapture.Add(Attributes.DefenseMultiplierDef);
          RelevantAttributesToCapture.Add(Attributes.AttackMultiplierDef);
    }
    NOTE: If you do wish to use InvalidScopedModifier, be sure to either remove/comment the part out when you wish to package the project or use preprocessor directives to make the compiler ignore the code outside of the editor. This is because the array InvalidScopedModifier seems to be compiled out for packaged builds, and as such the packaging will fail despite the fact that compiling regularly works perfectly fine.

    Compile, and voilĂ , it should now successfully capture attributes. You may check by opening up a GameplayEffect blueprint, and trying to select DamageExec as Execution class. It should allow you to view and select a few more settings. Add a new element in the array CalculationModifiers, and you should see BaseAttackPower, DefenseMultiplier and AttackMultiplier as valid Backing Capture Definition(not Health, however, as you have rendered it as hidden by adding it to InvalidScopedModifierAttributes). These are these calculation-only modifiers I talked about. Basically, you can now easily define each gameplay effect's BaseAttackPower individually by adding/setting BaseAttackPower to a value of choice.

    Well, but that wouldn't really do anything right now. We have set up capture definitions, but we haven't really set up any functionality quick. We will fix this really quick. Declare the function "virtual void Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams, OUT FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const override" in your header, and create a fitting definition in your cpp file. I will just copypaste an excerpt from Dave's damage calculation from his example project, and will add comments and changes where appropriate for our level of wisdom and our current setup of attributes.

    Code:
    void UDamageExec::Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams, OUT FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const
    {
    AttStruct Attributes; //Creating the attribute struct, we will need its values later when we want to get the attribute values.
    
    UAbilitySystemComponent* TargetAbilitySystemComponent = ExecutionParams.GetTargetAbilitySystemComponent(); //We put AbilitySystemComponents into little helper variables. Not necessary, but it helps keeping us from typing so much.
    UAbilitySystemComponent* SourceAbilitySystemComponent = ExecutionParams.GetSourceAbilitySystemComponent();
    
    AActor* SourceActor = SourceAbilitySystemComponent ? SourceAbilitySystemComponent->AvatarActor : nullptr; //If our AbilitySystemComponents are valid, we get each their owning actors and put them in variables. This is mostly to prevent crashing by trying to get the AvatarActor variable from
    AActor* TargetActor = TargetAbilitySystemComponent ? TargetAbilitySystemComponent->AvatarActor : nullptr; //a null pointer.
    
    const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec();
    
    const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
    const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags(); //Some more helper variables: Spec is the spec this execution originated from, and the Source/TargetTags are pointers to the tags granted to source/target actor, respectively.
    
    FAggregatorEvaluateParameters EvaluationParameters; //We use these tags to set up an FAggregatorEvaluateParameters struct, which we will need to get the values of our captured attributes later in this function.
    EvaluationParameters.SourceTags = SourceTags;
    EvaluationParameters.TargetTags = TargetTags;
    
    
    
    float Health = 0.f;
    //Alright, this is where we get the attribute's captured value into our function. Damage().HealthDef is the definition of the attribute we want to get, we defined EvaluationParameters just above us, and Health is the variable where we will put the captured value into(the Health variable we just declared)
    ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(Attributes.HealthDef, EvaluationParameters, Health);
    
    float BaseAttackPower = 0.f;
    ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(Attributes.BaseAttackPowerDef, EvaluationParameters, BaseAttackPower); // We do this for all other attributes, as well.
    
    float AttackMultiplier = 0.f;
    ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(Attributes.AttackMultiplierDef, EvaluationParameters, AttackMultiplier);
    
    float DefensePower = 0.f;
    ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(Attributes.DefenseMultiplierPowerDef, EvaluationParameters, DefenseMultiplier);
    
    
    
    //Finally, we go through our simple example damage calculation. BaseAttackPower and AttackMultiplier come from soruce, DefensePower comes from target.
    float DamageDone = BaseAttackPower * AttackMultiplier * DefensePower;
    //An optional step is to clamp to not take health lower than 0. This can be ignored, or implemented in the attribute sets' PostGameplayEffectExecution function. Your call, really.
    DamageDone = FMath::Min<float>( Damage, Health );
    
    //Finally, we check if we even did any damage in this whole ordeal. If yes, then we will add an outgoing execution modifer to the Health attribute we got from our target, which is a modifier that can still be thrown out by the attribute system if it wishes to throw out the EffectExec.
    if (DamageDone > 0.f)
    {
    OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(Attributes.HealthProperty, EGameplayModOp::Additive, -DamageDone));
    }
    
    //Congratulations, your damage calculation is complete!
    With that, your new damage calculation is now complete. You are free to try to use it through an instant GameplayEffect of choice. Set up a widget or debug string somewhere to tell you your current HP, set the BaseAttackPower of your GameplayEffect's execution to something high like 100, do not forget to assure that the multiplier attributes are not 0, and if your effect reduces your HP, then congratz, you got your very first damage execution calculation running. You can expand it as you please, as the ExecutionParams parameter of your Execute contains all info about your target, source and GameplayEffectSpec. Want to multiply your damage by your GamepalyEffectSpec's level? Easily done. Have an if-statement somewhere that says "if the target actor has tag X that is described to grant him immunity to all damage, reduce the damage of this calculation to 0" if you need such a thing for your game. It is easy to extend this damage calculation once you get it running initially. Add new multiplier attributes for fire or ice or whatever damage, and have it so that your execution checks for tags on either the source or the EffectSpec itself that say whether to consider these or not. Be creative! You have the tools, you have the power!

    EDIT: An aspect I forgot to mention, and that doesn't quite fit into this example, is that calculations can be used to decide if conditional gameplay effect classes attached to a particular gameplay effect should be applied upon calling of the execution, calling the function MarkConditionalGameplayEffectsToTrigger() from the Execute_Implementation function's parameter OutExecutionOutput. For example, you may use calculations to determine if the user's health is below half their maximum health, and call a GameplayEffect granting them a stats buff accordingly. It can be as simple as a simple stat check to as elaborate as a calculation that refuses to apply the conditional gameplayeffect without a certain effect asset tag while applying copies of the owning gameplay effect with this asset tag, simulating an aura-effect that doesn't affect the owner with just a GamplayEffectExecution alone(this example is a little out there, though. Cut me some slack, thinking off a usage example for every other aspect of the system is hard).

    EDIT 2: Another thing to keep in mind is that executions are not set up to predictively run for a client. As such, their effects will only show themselves to the causing actor when the server receives it.

    Gameplay Events

    Alright, this should about be the last major component of the system we haven't extensively talked about. Gameplay Events are amazingly useful due to their ability to trigger abilities without messing around with the ability owner's tags, while at the same time providing the GameplayAbility in question with a useful payload that may contain the source, target actors, magnitude and even generic object pointers for abilities to use as parameter. They're great if you have generic events and situations many abilities will call or listen for. A damage execution may for example throw out a GameplayEvent before damage is applied so that abilities can react with damage-decreasing buffs or pre-damage heals, and one after all multipliers and reductions so that abilities can take the final damage done to, for example, heal the source in proportion to the dealt damage(which would be a simple implementation for lifesteal). Gameplay Events are amazingly flexible, and most kinds of reactionary passive abilities can be implemented with well-implemented Gameplay Events in globally used functions and executions.
    You don't really create a new GameplayEvent in the same way you create a new class. They're in fact just mere data structs, and use a tag to tell abilities what kind fo event they are. It is up to the abilities themselves to react to them appropriately.
    The struct responsible for GameplayEvents, the FGameplayEventData, has the following variables:
    • EventTag: The tag that the event uses as label to be identified by. Do note that an event with tag label X will NOT actually be call all GameplayAbilities using this tag as trigger. More on that later.
    • Instigator: An actor pointer to point to the source or instigator with. Due to the nature of events, you can place any actor reference here, or even leave it null, but it never hurts to put a fitting actor reference here.
    • Target: Same as instigator, but for targets. Personally, I always set this to the actor we call the gameplay event for, because, I mean, that IS pretty much the target of the gameplayeffect. That said, if you for example have an event that tells a damage source it dealt damage to a target, you can switch it around like that too. It's your call, you are given pretty much no limits or guidelines in this struct.
    • OptionalObject and OptionalObject2: UObject pointers that can be filled with references for extra info. Maybe you want a GameplayEvent in your own child or GameplayAbilities you inherit all your other spells from that implements at the initial activation, taking the GameplayAbility object itself as parameter? That would be a possible example.
    • ContextHandle: GameplayEffectContextHandles are the part of effect specs that store the origin of an effect, such as the ability they came from, the original creation point in the world, the owner of the effect. These all can be useful for the GameplayEvent itself, so add this parameter when you can.
    • InstigatorTags, TargetTags: The tags the instigator and target had during the initial calling of the GameplayEvent. This is different from getting the tags through the intigator/target pointers, as the tag containers through the pointers may update halfway through(obviously, due to being pointers). You should usually be able to set these because you usually have an instigator and target pointer you could use, as well. There is no shame in keeping these empty, however. I'm just saying. Better safe than sorry.
    • EventMagnitude: A singular float. You're more or less free to use it as you want. I personally use it as parameter for my damage events, setting the magnitude to the calculated damage up to the particular step in the calculation(I have an event before all calculations, after bonus multipliers, after resistances, etc.). This is just an example, though..






    GameplayEvent structs do not have a constructor that parameterizes these, so you need to set these manually. A little annoying, but you can set up functions to help with that.

    Alright, now that we have a GameplayEvent struct, it's time to trigger abilities with it. Abilities may set up a trigger by going to their class defaults and adding a trigger with trigger source Gameplay Event and your tag of choice as values.
    We actually can go two different paths to call a GameplayEvent for all abilities in an ability system component. We may either call the static function SendGameplayEventToActor(AActor* Actor, FGameplayTag EventTag, FGameplayEventData Payload) from the AbilitySystemBlueprintLibrary class(which is pretty much just one big class of convenience methods exposed to blueprints), or just call the ability system component's HandleGameplayEvent(FGameplayTag EventTag, const FGameplayEventData* Payload) function directly. AbilitySystemBlueprintLibrary's function is safer and more convenient to use, though our actor needs the IAbilitySystemInterface implemented for it to work properly. It also does not return the amount of abilities that got triggered by the particular EventTag we use as parameter, though chances are most abilties and systems will very rarely need it. As such, using AbilitySystemBlueprintLibrary's function is often a better idea.
    Either way, the meaningful parameters of both functions boil down to and FGameplayTag EventTag and the GameplayEvent struct itself, which is usually called Payload. The payload is self-explanatory, as this is what we will give our ability to work with when called from GameplayEvent. The EventTag is the tag that the ability system component to try to trigger all abilities by. This tag and the tag you give to the struct as label must not be the same thing. For my own usage they usually are, but nothing stops you from having separate tags for event calling and tags for event labelling.

    Alright, so if you did everything correctly, your ability should respond to a GameplayEvent of choice(this is easily testable by assigning a random key input on your character to send the event with the event tag of choice to your character, as it should have implemented the interface a long time ago by now). That's fine and dandy, but where is the payload? Well, going back to the GameplayAbility blueprint for a little, you may or may not have noticed already that there is a different ActivateAbilityEvent defined, but not added to the event graph by default. The event is called ActivateAbilityFromEvent, which does have a Gamepaly Event data struct as input.
    Your first thought may be to set up a separate chain of blueprint nodes that start from the ActiveAbilityFromEvent node, but this is wrong. The reason for this is not at all simple and in fact rather bizarre, because GameplayAbility's constructor is set in such a way that it will set a hidden bool to use the struct-less ActivateAbility node for all ability activations when it is present in the blueprint graph of an inherited blueprint class. I told you man, witchcraft! This module is witchcraft!
    Essentially, you will have to replace the ActivateAbility node with the Event activation event in your GameplayAbility blueprint. This surrenders your ability to call your ability through conventional means which does not provide the ability with a Gameplay Event struct to work with(such as via action mapping or through TryActivate functions), but you may be okay with this if the ability is meant to be purely passive and response-based. If you want an ability that does need both gameplay event structs when called via Gamepaly event while still being eligible for manual activations with action mappings/TryActivate, you're best off just splitting active and response-based ability activations into separate abilities that are usually delivered in one bundle.

    Thus, the rough overview of GameplayAbilities is complete

    Not complete as in, say, there isn't more to it or I won't update this guide a little bit further down the line as more intel keeps pouring in(right now, how attributes are best replicated is a bit vague) and I have more time to write down concrete examples for how to use certain systems, but you now have a somewhat complete overview of the module's core systems, as well as their helper systems that allow better interaction between each of them.

    In case of questions I would recommend checking out the #gameplay-abilities-plugin channel on the UE4 Discord. I personally am not as active on here as I used to be, but the people on there are continuously picking on the plugin themselves and are generally helpful if you are in need of troubleshooting.

    If you want to further your understanding of the more niche aspects of this system, I do recommend DamirH's analyses, thoughts and ramblings on the other aspects of the system that have not been thoroughly covered here. It will be your best bet for advanced insight into the system's more niche aspects as well as discussions involving them, as the guide written here aims to be more of a simple introduction to the system and a step-by-step to get its core running, and I consider it to be more or less complete in this regard.
    Last edited by KZJ; 02-12-2019, 11:50 PM.

    #2
    Will you marry me? Thank you so much for this man i've been looking for a guide for months while trying to figure out the best way to use this system.

    Comment


      #3
      That was the main motivation. I myself had been looking for a guide for a month or two until I actually found the sample project Dave Ratti from Epic provided. I have no idea why it doesn't show up on google or any search whatsoever anywhere, but someone on reddit just casually linked it like "oh btw this exists, just saying". Since I got a chance to get the initial setup up, I could experiment and trial-and-error until I sorta figured out the rest of the system.

      Since I seemed to seemingly be the only person on the planet to both have a passable understanding of this monstrosity and the willingness to write it down somewhere, I figured I could maybe spare other people from this madness. So here I am.

      Comment


        #4
        It would have been great to know about the sample project when starting. Epic should add a link to in on the GameplayAbility docs. The GameplayEffectsTests.cpp is old and not up to date code wise. That's what i've been using up til this point.

        Comment


          #5
          Thanks for the writeup, the initial setup has been the biggest hurdle. You do however have a few typos:

          - You use AbilitySystemComponent at one point, AbilitySystem at others.
          - It's not InitActorInfo, it's InitAbilityActorInfo

          That being said, I am extremely disappointed to see such strong coupling of the input to such an enum. Actually, I'm very disappointed to see it requires input binding at all. I want to activate my abilities manually through my own system, this feel like going against the grain of my entire infrastructure and design paradigm (that being record input in dedicated functions only, process it in Tick()). I'll do some digging on my own to see how one would go about firing abilities manually without reliance on the BindAbilityActivationToInputComponent stuff.

          Edit:

          Ok so there are a few functions to manually activate abilities without binding them to input, namely:
          Code:
          	/** 
          	 * Attempts to activate every gameplay ability that matches the given tag and DoesAbilitySatisfyTagRequirements().
          	 * Returns true if anything attempts to activate. Can activate more than one ability and the ability may fail later.
          	 * If bAllowRemoteActivation is true, it will remotely activate local/server abilities, if false it will only try to locally activate abilities.
          	 */
          	UFUNCTION(BlueprintCallable, Category = "Abilities")
          	bool TryActivateAbilitiesByTag(const FGameplayTagContainer& GameplayTagContainer, bool bAllowRemoteActivation = true);
          
          	/**
          	 * Attempts to activate the ability that is passed in. This will check costs and requirements before doing so.
          	 * Returns true if it thinks it activated, but it may return false positives due to failure later in activation.
          	 * If bAllowRemoteActivation is true, it will remotely activate local/server abilities, if false it will only try to locally activate the ability
          	 */
          	UFUNCTION(BlueprintCallable, Category = "Abilities")
          	bool TryActivateAbilityByClass(TSubclassOf<UGameplayAbility> InAbilityToActivate, bool bAllowRemoteActivation = true);
          
          	/** 
          	 * Attempts to activate the given ability, will check costs and requirements before doing so.
          	 * Returns true if it thinks it activated, but it may return false positives due to failure later in activation.
          	 * If bAllowRemoteActivation is true, it will remotely activate local/server abilities, if false it will only try to locally activate the ability
          	 */
          	bool TryActivateAbility(FGameplayAbilitySpecHandle AbilityToActivate, bool bAllowRemoteActivation = true);
          Edit 2:

          Sure enough, it works:

          Click image for larger version

Name:	Govbi10.png
Views:	1
Size:	67.9 KB
ID:	1123586

          It did require a 1 frame delay, otherwise it wouldn't work from BeginPlay through.
          Last edited by DamirH; 02-19-2017, 05:57 PM.

          Comment


            #6
            Needless to say, I do apologize for all typos in advance. AbilitySystem is not actually a typo however: The AbilitySystemComponent variable's name is AbilitySystem, and it's usually clear when I talk about the AbilitySystemComponent so I was being a little lazy on some ends. If it bothers people, I could just change all mentions to AbilitySystemComponent later on. I am however grateful for the input regarding InitAbilityActorInfo. I will fix this immediately

            Input binding is recommended, but not 100% required, as you can just use the TryActivateAbility functions if you want to(I did mention them once during the setup, and I actually wanted to properly explain them in the next section), for example, trigger them by pressing an icon instead. However I am unsure how certain ability tasks that request the action mapping being pressed/released would react to this. I don't think that'd work for example. You'd have to try yourself. I guess worst case you could just make do by using the Confirm/Cancel binds instead, which you should be able to fit into any game, frankly.

            Other than that, the enum bindings have the same restrictions and strengths as any regular input binding; someone was probably just feeling fancy doing that enum nonsense.

            It seems odd that you need the frame delay in BeginPlay though. I'm on the phone so I can't check, but there is surely a better way to do this.
            Last edited by KZJ; 02-19-2017, 06:56 PM.

            Comment


              #7
              Originally posted by KZJ View Post
              Needless to say, I do apologize for all typos in advance. AbilitySystem is not actually a typo however: The AbilitySystemComponent variable's name is AbilitySystem, and it's usually clear when I talk about the AbilitySystemComponent so I was being a little lazy on some ends. If it bothers people, I could just change all mentions to AbilitySystemComponent later on. I am however grateful for the input regarding InitAbilityActorInfo. I will fix this immediately

              Input binding is recommended, but not 100% required, as you can just use the TryActivateAbility functions if you want to(I did mention them once during the setup, and I actually wanted to properly explain them in the next section), for example, trigger them by pressing an icon instead. However I am unsure how certain ability tasks that request the action mapping being pressed/released would react to this. I don't think that'd work for example. You'd have to try yourself.

              Other than that, the enum bindings have the same restrictions and strengths as any regular input binding; someone was probably just feeling fancy doing that enum nonsense.

              It seems odd that you need the frame delay in BeginPlay though. I'm on the phone so I can't check, but there is surely a better way to do this.
              Well I wouldn't trigger them in begin play anyhow. My issue is that my inputs are actually more like fighting-game inputs, so it's not just a single input per ability. This makes the enum stuff completely unusable. What are these ability tasks that require action mappings that you refer to?

              Also, sorry about the AbilitySystemComponent, I wasn't clear - In your BeginPlay() function you do if(AbilitySystemComponent ) {.... AbilitySystem.GrantAbility() .... }.

              Comment


                #8
                No no, I don't think the ability tasks I'm talking about would help you with this. They're mostly for waiting for a confirm/cancel input, or waiting for the ability input to be released/ pressed again. What you could do is assign a unique ability to all inputs you want to use with a unique tag each and then just listen in an ability if the other ability is being pressed, perhaps cancelling that newly called ability in the process.

                I do not know about the specifics of your fighting game-esque abilities. so it is hard to really suggest a proper solution of what you wanna do, but perhaps using gameplay effects that grant you new abilities the further you get into your combo could be a different approach? I am fairly sure that whatever you're trying to do has a proper good solution, if perhaps a little roundabout.

                Comment


                  #9
                  Oh I already have an established system for reading my inputs. I also have a very detailed abilities and skill system that includes GameplayTags too. But all of that is reinventing the wheel - if GameplayAbilities are usable and working as intended, I was gonna consider switching to them, as I can afford doing that in this early project stage. I am not asking for any specific instructions, just analyzing the compatibility and general workings of the module.

                  Due to my input structure I need the module to be able to fire off an ability after my input is complete, which it seems it can do. As such I'll have to research it a bit more... funny enough it seems to do something very similar to what I've been implementing and refining for the past ~2 years (namely that abilities are individual Blueprints that can spawn stuff like sounds and particle effects, have delayed effects after activation (fireball waiting for animation to finish). I just call them payloads though... the ability gets activated but its payload is delivered via an animation cue. In any case, my system doesn't do any networking or replication since I didn't require it. I'll spend a few days digging through the GameplayAbilities to gauge if it's overkill for me or if there are features worth switching for. The increased coupling with GameplayTags is interesting.

                  Comment


                    #10
                    Hey KZJ i'm trying to make an effect that affects multiple actors at the same time and splits the damage between all the affected actors. Any tips?

                    I was thinking about adding a TargetCount to the attribute set and update that myself but i'm thinking that there is a better way to do it

                    Comment


                      #11
                      I'm assuming you use a multiline trace within an ability to find your target actors, and apply the damage as modifier?

                      One possibility is to wait for me to explain GameplayEffectExecutionCalculations, which would make this a relatively simple endeavour as the execution can just check for the effect's level(which is a float, and doesn't seem to really be used for anything else except possible GameplayCue magnitudes so your own executions and such could freely use it) and multiply the damage of the effect accordingly(which wouldn't just be useful for this ability either. All potentially scaling damage can benefit from such a setup). You would then do the multiline trace, divide your arbritrary total damage value by its length(aka the amount of actors you hit) and then applying a damage gameplay effect spec with the level totaldamage/HitResultLength.
                      You could do the same with just modifiers actually, as a 2 stack of a gameplayeffect will double the modifier's effect, but you'd have to use stacks, which are only ints, so if your total damage cannot be smoothly divided by your amount of targets, then it'll need to round.

                      If this is, say, a DoT effect that will dynamically split damage to bystanders each pulse/period, then that'd be a little more complicated. You could do this through a Granted GameplayAbility in the effect, or a GameplayEffectExecutionCalculation. The GameplayAbility would need something to trigger it in these pulses though, so you'd probably need a different GameplayEffect with a particular tag or just a GameplayEffectExecution as trigger, and the effect would perhaps be a little too specific to be implemented into a GameplayEffectExecution alone.

                      Hm, you'd probably need to give me some more specifics.
                      Last edited by KZJ; 02-20-2017, 09:17 AM.

                      Comment


                        #12
                        Thanks for the doc. You might post it into the Wiki later on, this should belongs to docs.

                        This system is just an amazing gift from Epic,but as you said, the understanding is "hard" but you can do a lot with it once you setup it properly.
                        I think you may not have mentionned enough, but from what I read, it's multiplayer ready so the client input component will trigger the the ability and should run the stuff on server side. What I do not know as of today if there is something trigger immediatly on "client side" like FX or if it is triggered when the server said it's ok.. so with at least a ping delay.
                        I meant do you know if on client side there is a simulation on going that is "drive" by server answered. For example you call ability 1 that put a fx of fire, your client side cooldown is ok but not the serverside of it. what will happenned?

                        thanks for the good job on this!

                        Comment


                          #13
                          I do not know the specifics of the replication other than the fact that "it just works", and it's kinda out of the scope of the guide(which is more about getting the initial setup, which is hard and more or less requires proper explanation from either someone or an example project, and elaborating on what systems the module has, how to use them and how they play off each other). The ability will first call itself clientside, expecting the server to soon call itself too. From the little testing I did with the net console commands, if the server calls itself with a certain delay(or perhaps at all delays, it just wouldn't be noticable with slight delay), it will then roll back the events of the client side ability call and then make its own. This means Client Activation->Client Cooldown plays->Server Activation->Server syncs client cooldown with itself. If the client has enough delay to fire the ability twice, the server will execute it twice, too. Presumably, if the lag varies enough that the client activations fulfil the cooldown time but not the servers, the server activation will roll back the client activation, and finally sync the activation as if it had failed past the Commit Ability node(which checks cost, cooldown, etc.). The server likely does not roll back the client activation all at once, as there are ability tasks concerned with syncing client and server ability activation to a certain point.

                          A GameplayCue firing in that time(which would be the fire fx, most likely), though, will play only once during the client activation, because GameplayCues are set up in a way that they will neither be replicated, nor really "corrected" by the server(correcting a temporary visual effect would be very silly anyway, you've already seen it). That's why it's important for GameplayCues to really only be cosmetic effects. That being said, the gameplay cue will play for other clients and the server itself(when not dedicated) during the server activation. Ultimately, it works out in such a way that GameplayCues will only ever be seen/activated once per player.

                          I wouldn't know about the wiki, or the fact that they allow user-made guides in that. Where do I find it and how do I post there?
                          Last edited by KZJ; 02-20-2017, 11:49 AM.

                          Comment


                            #14
                            For the wiki: https://wiki.unrealengine.com/Get_In...ding_New_Pages

                            Thanks for the additional information. I bet the tool is rocking solid, they working with it since months not to say years, so I think we see most of the "edge cases". I wish this was at this level 1.5 years ago when I started working on my game as I had to implement all of that... and not as good as they did but it's working for my game!

                            This is a really good documentation to start, with code samples, everyone will really appreciate!
                            There are a lot of small tasks to do to setup it that properly and it's great that you describe them step by step!

                            Comment


                              #15
                              Originally posted by KZJ View Post
                              I'm assuming you use a multiline trace within an ability to find your target actors, and apply the damage as modifier?

                              One possibility is to wait for me to explain GameplayEffectExecutionCalculations, which would make this a relatively simple endeavour as the execution can just check for the effect's level(which is a float, and doesn't seem to really be used for anything else except possible GameplayCue magnitudes so your own executions and such could freely use it) and multiply the damage of the effect accordingly(which wouldn't just be useful for this ability either. All potentially scaling damage can benefit from such a setup). You would then do the multiline trace, divide your arbritrary total damage value by its length(aka the amount of actors you hit) and then applying a damage gameplay effect spec with the level totaldamage/HitResultLength.
                              You could do the same with just modifiers actually, as a 2 stack of a gameplayeffect will double the modifier's effect, but you'd have to use stacks, which are only ints, so if your total damage cannot be smoothly divided by your amount of targets, then it'll need to round.

                              If this is, say, a DoT effect that will dynamically split damage to bystanders each pulse/period, then that'd be a little more complicated. You could do this through a Granted GameplayAbility in the effect, or a GameplayEffectExecutionCalculation. The GameplayAbility would need something to trigger it in these pulses though, so you'd probably need a different GameplayEffect with a particular tag or just a GameplayEffectExecution as trigger, and the effect would perhaps be a little too specific to be implemented into a GameplayEffectExecution alone.

                              Hm, you'd probably need to give me some more specifics.
                              So i'm working on a turn based rpg (think final fantasy mystic quest) that has abilities that can target all the enemies and when it goes to calculate the damage it divides by the target count (AttackPower * 4 - Defense * weakness / TargetCount). So i was trying to figure out the best way to store and determine the target count. One issue is that abilities are triggered in queue so the target count will need to be updated when the ability is triggered but since the effect will be triggered for each enemy the target count would get updated while it runs killing the enemies modifying that count which i dont want (i could also not kill the enemies until the ability is done running which is also an idea). Using the game effect level is a good and simple solution since i wont be using the level for anything else. Thanks for that

                              Comment

                              Working...
                              X