Help with a particular implementation of GAS.

Hey! Thanks anyone who takes the time to answer.

I am really struggling to understand how to properly set up the Gameplay Ability System in my project. It is a MOBA but it needs to be structured more similar to an RTS. (camera in the sky, possible to control multiple units in certain cases)

there is loads of documentation on how to set up and init the ASC on both the player state and on characters themselves, but there is not much on how to handle ownership and avatars in a situation where the player ASC (in this case playerstate), needs to be able to command an ai controlled pawn as its avatar from the sky.

I have tried to have the AI controllers of each unit have thier own asc with thier own abilities but it really struggles to connect to the the player properly and i cant use much of the functionality with Spec handles to populate the ability bar on my players widgets.

now i am attempting to implement the ASC on the playerstate instead and have the unit as the avatar. however i am stuck on how this should be set up. With this structure, where would i init the asc on the server/client? Should I have the units also init on possession with the AI controllers as thier owners and leave it? should I override ownership of the unit to the playerstate so both client and server see the playerstate as the owner and the unit as the client?

A bonus question would be, how would a structure like this handle switching units? if i select a different pawn that i own, how do you typically handle avatar switching and granting/removing abilities between them?

im just mostly confused on the typical structure for ownership when doing a top down controlled setup and would appreciate anyone who knows the basics of GAS letting me know.

Your NPC Pawns should each have their own ASC to handle attacks, damage, buffs, etc. I wouldn’t put the ASC on the Controller. Your pawn’s owning their own ASC/Attributes lets you possess different pawns at will with your PlayerController or let the AIController handle the pawn behavior. The attributes/abilities stick with the pawns.

Your custom PlayerController should have the logic for switching between pawns. You don’t need GAS for this, just create and call a Reliable Server Event to do it.

If you need an ASC player-centric use add another to your PlayerState for handling attributes/abilities not related to the individual ‘avatars’ or NPCs. I would skip this initially, and add it ONLY if your needs aren’t met doing ASCs on the pawns.

So that is how I had set it up initially but due to the fact that i technically don’t “Own” any of the units ASC, I cannot actually get any of the units spec handles for abilities. Not only does it make widgets not really work because I don’t have access to the cooldowns or any information on the unit. But it also seems to mess up the delivery of target data and things like that. Functions like WaitForTargetData don’t work because I can’t seem to pass information into the units ASC either. Is there a way around this or is this just not in GAS standard capability?

Not sure I understand… you don’t need to ‘own’ the ASC to have access to it. Get a ref to your pawn, get a ref to it’s ASC. Call what you need from there.

If you need info on cooldowns or other GE for your UI, you can use GetActiveGameplayEffectsForQuery or GetActiveEffectsWithAllTags with a reference to the NPCs ASC. GetFloatAttributeFromAbilitySystemComponent lets you access all your attribute values from wherever you need. Use TryActivateAbilityByClass or TryActivateAbilitiesByTag to call you abilities.

I am having a problem very similar to this one in this thread.

Calling TryActivateAbility from Player Controller (GAS) - Development / Programming & Scripting - Epic Developer Community Forums

Same issue mostly. I am unable to call functions such as GetAllAbilities or anything that uses the Gameplay ability Spec or Gameplay ability spec handles. as it seems that it will not replicate that from an server ai controlled ASC to my player in any way.

I havnt tried GetActiveGameplayEffectsForQuery or GetActiveEffectsWithAllTags yet. these might be the work around I need.

This seems like a setup issue. GetAllAbilities should work… Are you sure you’re calling these after the pawn’s abilites/attributes have been initialized?

How and where are you initializing your actorinfo, attributes and abilities?

I am developing a game right now that uses almost exactly the setup for GAS we are talking about. It’s very possible, just need to find the problem.

When I was attempting that approach, I was Initializing with a function at the time i distribute the unit to the owner playerstate.

That function looks like this in C++

I was init on the client OnRep_Owner it looks like though looking back maybe this won’t work right.

I am not an engineer so maybe this is where I was getting tripped up.

OK, here’s the simplified version of how I set up my NPCs for use with GAS:

NPC pawn actor header has ASC and AttributeSet and implements the IAbilitySystemInterface

Constructor is setup like this:

	AbilitySystemComponent = CreateDefaultSubobject<UMyAbilitySystemComponent>(TEXT("AbilitySystemComponent"));

	AbilitySystemComponent->SetIsReplicated(true);
	AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Minimal);
	
	AttributeSet = CreateDefaultSubobject<UArmoredHealthAttributeSet>(TEXT("ArmoredHealthAttributeSet"));

Beginplay:

Super::BeginPlay();
if (AbilitySystemComponent)
{
	AbilitySystemComponent->InitAbilityActorInfo(this, this);
	
	InitDefaultAbilities(); //custom fcn that gives abilities
	InitDefaultAttributes(); //custom fcn that applies default GE
}

That’s pretty much it. FYI I don’t use an AI Controller with my NPCs, I use a statetree component to handle AI logic within the pawn itself.

If you’re using an AI controller you can Override PossessedBy to setup after the AIController has possessed the pawn:

void AMyCharacterBase::PossessedBy(AController * NewController)
{
	Super::PossessedBy(NewController);

	if (AbilitySystemComponent)
	{
		AbilitySystemComponent->InitAbilityActorInfo(this, this);
	}
	SetOwner(NewController);
}

To do setup when you possess/unpossess the pawn with your PlayerController (if you actually need it), override the PossessedBy, like above but add check to test for controller type. Change your SetReplicationMode to Mixed from Minimum if going from AI to player control (and vice versa) so things replicate over as the owner. Read the comments on those modes to understand what they do, but generally minimum just reps the very basics, mixed is used for replicating things relevant to ownership.

If you are just sending commands to NPC units using ‘Camera in the Sky’, you don’t need to possess them. Only possess if you are controlling them as an actual avatar. Otherwise, you can just command the units by doing ‘tryactivateability…’ calls when you get a ref to their ASC if you want to use GAS for the commands.

You don’t need a playerstate ASC for any of this. You may find an eventual use for one, but get the NPC stuff working well, then expand if you need to.

Looking deeper into this, you may need Mixed rather than Minimal for replication to get cooldown info as GE’s aren’t replicated at all in Minimal mode, only tags, attributes and cues. Keep an eye on your network use if you do this as it could be problematic with lots of units, but setting to mixed for initial prototyping shouldn’t be a problem, and you can change the mode in the units depending on what units you’re focusing on.

Ok ill take a bit more time to see if i can get this to work. I dont recall exactly where things went wrong but im pretty sure i had a setup almost exactly like this to start with and it just refused to replicate to me. I suspect your setup might work because you actually are possessing the unit in question. i think the disconnect actually happens due to the AI controller in the middle.

a few questions on your setup. it looks as though you are calling your initialization on BeginPlay only? this would initialize on the client but not the server no? if so where are you initializing on the server? If this does initialize on the server as well then how are you handling ownership? wouldnt begin play trigger before a playerstate or controller could be assigned as its owner? that would make its owner itself on initialization. if it owns itself or had no owner on begin play, i didnt think it would ever give over ability specs to clients assigned ownership late.

BeginPlay happens on client and server. Anything that is networked will be overwritten on the client by server values.

What ownership? By the controller? In my situation, not using an AIController. If you are, you can init in the PossessedBy function like i showed in my previous post, in addition to BeginPlay and do whatever setup you need there. This will run every time a new controller takes control of the pawn.

FYI Controllers are just actors with a special relationship to the pawn, controllers are useful but optional for NPCs. There is only the one PlayerController on a client for the current player (unless its a local co-op game). PlayerState is also an actor, one for each connected Player to network the player’s state to clients, they don’t exist for basic actors/pawns.

The ownership thing might be hanging you up a bit, or maybe I misunderstand. You as a player don’t need to own things to access them, you use your custom PlayerController and whatever Pawn you’re ‘inhabiting’ to interact with the world. You can access global systems like the collision system for traces or GetWorld for access to it’s actors.

For your situation, as the Player my ‘main avatar’ i inhabit would be a custom SpectatorPawn lets call it CommanderActor. Use this to fly around, lock position to units, select NPC units, send commands using the units ASCs… all that stuff. Your logic for this will be in your PlayerController, and/or in the CommanderActorBP.

The NPCs ‘own’ themselves (or technically they are generally owned by they Level which is owned by the World). You need to find a way to select them via a squad manager UI, screen picker, GetActorOfClass, etc, but once you do, you have a reference to the NPC actor object therefore a ref to its ASC, get that, call your GA, apply GEs, send GameplayCues, access attributes, etc.

So i mean specifically ownership by a specific player client. Since InitAbilityActorInfo takes in (Owner actor, Avatar Actor). if you call that on begin play before any players have connected. what is it taking in as its Owner actor? there are no controllers or player states present yet most likely. Since you mentioned that Mixed replication also is used to replicate to owners. that would also require some level of initialization with the correct owner player right?

Either way, im starting to suspect that i may have gotten too deep in the weeds with proper ownership distribution thinking that my issue stemmed from the AI controller disrupting it, but may have been having issues with my ASC not being initialized on the client properly to begin with. Especially if you are having no problems with any of its functionality and are using it this way.

InitAbilityActorInfo(this,this)… the ‘ownerActor’ is itself. No controllers or playerstates need be present. The ASC is a component of the NPC PawnActor and doesn’t depend on PlayerStates or Controllers at all. The ASC takes care of the networking for you… thats a big part of its job. You have to tell it what to replicate and to who via replication mode and how you set your attributes up.

By ownership do you mean like belonging to a player, on their team, under their potential control? If that’s the case, you might add a gameplaytag variable to your NPC class/BP called Team and use it to filter results/determine actions. A connecting player wouldn’t actually ‘own’ those NPCs, you have to create some kind of connection like sharing the same gameplaytag for instance. This is a setup issue on the pawn, not a necessarily a GAS thing. The ownership concept with the ASC/ActorInfo is more about network routing and references for GA/GEs

Edit: In this instance I would have a GameplayTag variable Team on the PlayerController I would check against. You have to come up with the logic to initially assign the Team to a PlayerController when they join, and assign the var to the NPC when spawned.

ah i see. and yes thats what i meant. like belonging to a player, on thier team, and under thier potential control. I associated ownership with this idea loosely. Mostly because i assumed that because the replication modes were using “ownership” to decide what and who to replicate to, that it must be logically linked to potential player control and i would bundle them together down the line.

1 Like

Ok I deep dove back into this approach I was taking and ended up stuck at the same spot. maybe you can explain what might be happening here.

I now init on possession with the AI controller like you mentioned. (i also tested it on begin play and every other combination of owneractor/avataractor i could think of)

void ABaseCharacterAbility::PossessedBy(AController* NewController)
{
    Super::PossessedBy(NewController);

    // Initialize ASC on the server when possessed
    if (AbilitySystemComponent)
    {
       AbilitySystemComponent->InitAbilityActorInfo(NewController, this);
    }
}

and I init on the client now with OnRep_Playerstate as the documentation suggests to insure the client is connected first.

void ABaseCharacterAbility::OnRep_PlayerState()
{
    Super::OnRep_PlayerState();

     //Initialize ASC on the client when PlayerState replicates
     //This ensures the client's ASC knows about its owner for any future GAS operations
    if (AbilitySystemComponent && GetPlayerState())
    {
       AbilitySystemComponent->InitAbilityActorInfo(GetPlayerState(), this);
    }
}

as before the abilities trigger no problem when sending commands to my unit. HOWEVER, the GetAllAbilities function is simply not working from the client side. Making it so that i cannot call it in my player controllers HUD widget where I try to display that units abilities.

it is set up like this currently just for testing.

This is off the back of my selection logic client side

I click the unit and i get nothing.
I check the array and its returning 0 items. even though i know it has an ability that i can see in the debugger as well as i can activate it and it triggers.

I am also calling it in my player controller to see if it works there. Using a reference i get when clicking on the actor i want to select. Same results.

If i play as server. then they are fine and all the info is present. It simply doesnt replicate that information to the clients side so its impossible to pull that data on click from a client since they are clicking on their clients instance of that actor. This issue was not present when i was instead possessing the unit before i switched to overhead camera, i was able to pull that information into my widget. this is what ended up making me deep dive into the ownership side as i assumed due to the replication rules that Mixed would replicate all this stuff to the OWNING client as it says in the documentation. However. Now i remember I even tried it on full replication and i was still unable to GetAllAbilities on the client side.

That is odd. The abilities should network over to clients. How are you calling the NPC’s abilities from the client?

Why do you have the OnRep_PlayerState? are you possessing the ABaseCharacterAbility? if not, this will never fire… playerstate is associated with the possessed pawn only, it’s normally null on NPC units.

Just checking: make sure you’re calling that GetAllAbilities from the NPC unit’s ASC, not the PlayerState’s ASC. I’m assuming you are but cant be sure from the snippet.

Your controller has nothing to do with your ASC, you want this instead:
AbilitySystemComponent->InitAbilityActorInfo(this, this);

Edit: Make sure you do this on BeginPlay

OH wow. I just threw a breakpoint on my OnRep_PlayerState and only just now realized that you were right it never fired. I was trying to debug this whole time with it only initialized on the server. This might actually be my root issue I cant believe i never properly checked my client initialization. Hmm in this case, where is a good place to trigger the client side init?

ALSO

2 things that kind of throw me off here.

1. I also tried to just init on begin play for both client and server and it still had that issue. in this case im guessing because there are no clients connected yet on begin play?

  1. you also mentioned using possessed by on the server side instead of begin play due to having an AI controller before. why does this not matter now?
void APACharacterBase::PossessedBy(AController * NewController)
{
	Super::PossessedBy(NewController);

	if (AbilitySystemComponent)
	{
		AbilitySystemComponent->InitAbilityActorInfo(this, this);
	}

	// ASC MixedMode replication requires that the ASC Owner's Owner be the Controller.
	SetOwner(NewController);
}

You sent me a line very similar to this one to put there. and this one i pasted is directly from the documentation. and its the comment line right before SetOwner(NewController) that has me even more confused.

// ASC MixedMode replication requires that the ASC Owner's Owner be the Controller.

is this not the case and i can just throw it on begin play?

Using initabilityactorinfo in the possessedby was not the best example. My point was can init/re-init your ASC using the PossessedBy if you want to. I should have mentioned OnRep_Controller on client, too.

Good eye on the mixed mode comment. I copied that code out of a pawn I set up a while ago and didn’t think about why I set the owner like that. It’s already set in the parent function Pawn::PossessedBy so it’s redundant there.

So, looking into it, you’re right. You can’t get the ability info unless you are the owning client as it uses the owning clients controller relevancy to pipe the mixed mode data. Still looking at GEs/cooldowns but probably the same. Friggin bummer.

A solution for cooldowns would be to set a tag during the cooldown using the cooldown GE and use that tag to display UI as it will be replicated, but that doesn’t give you the cooldown time. You could maybe set the as an attribute to network over… a bit sloppy but its an option.

Ah dang ok thanks for verifying that. That original realisation was what lead me to go for having the ASC on the playerstate instead.

Theoretically does this idea work?

Playerstate has ASC. Selection of a unit triggers give relevant abilities to the ASC, what i was then hoping was that i could have my playerstate stay as the owner and then have a function that sets the AvatarActor to the selected unit. then my Playerstate ASC is firing the ability with all correct ownership, but just using the Unit in question as the avatar at the time to fire the ability from. then I could select a different unit and it gives its relevant abilities and swaps to that as its avatar.

I can confirm that it does fire abilities correctly in this setup (from the avatar unit). What i cant figure out is how to set up avatar switching properly, or if its even possible. as well as how and where to Initialize in a setup like this one. ←– more or less the first question that i started this thread with.