Download

Able Ability System Info and Support Thread

Generally input handling is so specific (for player’s and classes) and tends to be iterated on a lot, that keeping all that code in a single place in the Player is normally the cleanest way to go (or you set up a state machine that has various states each with different input rules).

I don’t mean to dissuade you from trying your approach and keeping input checking contained within the Ability - there’s just no current support for that so you’d have to create your own Task by inheriting from IAblAbilityTask. The editor and such will automatically find it once you create it. You can use a simple task like “Play Sound Effect” as an example.

I’ve managed so far by adding my own task in c++.
I have a different problem now.
Basically I have a climbing system that provides me with information about nearest edges that are climbable in my world. It provdes me with an array of points as a path over which I want to move my actor playing the active climb ability. Can I make an ability duration “variable” so that it depends on the task length which is basically runtime generated from number of points and distances between them?

Yes. You have two options:

1.) There’s is an Ability Play Rate that you can define at Runtime (via GetPlayRate), so if you create just a default Ability that is 1.0s you can simply scale it accordingly up and down based on the whatever length you need.

or.

2.) If you are using a custom task, you can look at the Play Animation Task which pulls the length from animation asset or the Query Tasks which actually simply ask the Task if it’s done (IsDone) yet as the task itself is an indeterminate length.

Thank you. I will try the IsDone() approach as it seems more clean to me

Another crash.


UAblAbilityTaskScratchPad* UAblAbilityContext::GetScratchPadForTask(const class UAblAbilityTask* Task) const
{
    UAblAbilityTaskScratchPad* const * ScratchPad = m_TaskScratchPadMap.Find(Task->GetUniqueID());

Seems it might be the m_TaskScratchPadMap that is being null at this point. Happens randomly whenever I exit from play mode.
Can’t tell what is null here as m_TaskScratchPadMap isn’t symbolized properly and Task seems to be ok. Unless its “this” that is null at that point.

Likely “this” is null. Can you give me repro steps so I can try and re-create this crash? Looks like you’re hitting exit while an Ability is running?

Here is the first thing before the crash from Call stack which is my custom task


class MYGAME_API UMoveUnitTask : public UAblAbilityTask

EDIT: I just checked, yes, when I exit the play mode while my task is playing, it crashes

EDIT2: Apparently I need to check if Context is valid before doing anything. My bad.
I was looking at how OnTaskStart is implemented. Altough it would be nice if a layer above, in whatever is calling “OnTaskEnd” would first check if context is valid, so that I don’t have to add the same check in every single OnTaskEnd myself.
Unless there is a reason for someone to call OnTaskEnd without the context?

Apparently I need to check if Context is valid before doing anything. My bad.
I was looking at how OnTaskStart is implemented. Altough it would be nice if a layer above, in whatever is calling “OnTaskEnd” would first check if context is valid, so that I don’t have to add the same check in every single OnTaskEnd myself.

I could put it higher. Ideally your context is always valid when you’re at that level. I remember adding that logic in the Blueprint call (basically if you pass bad parameters, we just error out and don’t attempt the Ability). Are you activating this via the C++ call? Or BP?

Task is activated with ActivateAbility Blueprint node.
Ideally the context is valid, yet for some reason when you hit ESC while an ability is playing you will get a crash because context is messed up. The same would happen with your built-in abilities, but I can see your code clearly adds a Context.IsValid() check inside OnTaskEnd() :slight_smile:

Ah, right. So your Context was valid, you closed the session which cleaned up all the objects, and then the context became invalid. Right, I remember having to add that check to fix that particular case.

Ok, here is a question. Why in the hell is BranchAbility marked as PURE ^^?
It’s an execution node, exposed to blueprint yet it has no exec input and output? Is this a mistake?

I mean this makes literally no sense :smiley:

That’s likely a bug.

Even if you take PURE as a const for Blueprint, it would fail that test as branching inherently changes the state of the Ability Component.

EDIT: Did an entire pass on the Ability component and moved most things from BlueprintPure to BlueprintCallable.

Another problem with networked branching abilities.
Setup:
BaseAbility - the only task it has is a branch task with custom ability chosen by GetBranchAbilityBP(). This branch is immidiate (the task is the only thing on full timeline). The ability duration is something short or even zero. Im using 0.01 just to make sure.

Now the abilities to which this can branch can be whatever, for example play a montage, just make sure they have some cooldown. Im using 0.1f

I put a PrintString inside “GetBranchAbilityBP” to see what’s calling it.
Now trying to activate it from server/client, sometimes it gets called twice, once for server and once for client and then the ability and then those abilities kinda get canceled and doesnt work.
Log is saying something like “Activating ability X” and immidately after “Could not activate ability X because it is on cooldown”

It seems to happen randomly. 50% of the time it works as intended and the other half it drops this into the log and doesn’t work as it should

Shouldn’t Branch Task be called on server only?

Hmm, I’ll try to repro that locally.

An update on the networked branching problem with cooldowns.
I’m not 100% sure, but (since branch task isnt exported :/) I had to recreate your branch ability giving myself possibility to change realm of that task.
Upon changing the realm to server only the bug is gone, which leads me to believe that branches should happen only on server as they seem to be replicated by the AbilityComponent itself. Though I might not be correct

Branch task is called on the client for prediction purposes. So what could be happening is the Client and Server both play the Ability (as both are running the Tasks in tandem). Client tells the server to branch to Ability Y, but Server is already on Ability Y, so it prints the error - but if that’s the case, the Server should be still playing the Ability.

Is this a listen server? Or dedicated server?

It is a listen server (if that’s what you mean by when you press play in editor and get 2 game windows).
It might be the thing you are saying though I just tried my more cases with branching and cooldowns. All my branches get messed up if i add any cooldown to the branched-to abilities.
Once I change branches to my own (server only ones) it works perfect.

Anyway for some reason if this warning happens then the ability isn’t playing out correctly. Animations get cancelled and other tasks kinda seem like only play out Task Started but not going anywhere as my own debug lines are drawn out, but the logic in tick isnt happening.

EDIT: I have a combo attack made just like in your video tutorial for sword combo, but adding cooldowns to second attack of the combo ability brings out the result i am talking about and the ability doesnt start on the server as I am able to walk normally on the server and the same character on the client plays out the ability but gets dragged around

Adding a log to the previous case

Again, about 50% of the time it eithre plays out correctly or i get this in my log and it fails on the server aka just doesnt play the ability.

Now the ----- logs are just PrintString nodes attached to OnTaskStartedBP and OnTaskEndedBP

EDIT:



void UAblAbilityComponent::HandlePendingContexts()
...
if (const UAblAbility* PendingAbility = m_PendingContext*->GetAbility())

I whenever it gets buggy I keep getting AttackCombo2 ability context duplicated on m_PendingContext list. There are 2 entries of that context. I’m not sure this is supposed to happen as a little later in the same function ActiveAbility gets nulled and the second ability won’t fire off since it’s still on cooldown?

Ok, I THINK found it. It’s exactly as you said. The Ability gets fired on both Server and Client exactly at the same moment since on a local machine there is zero lag, and it would work exactly the way you said, the server would play out the ability anyway BUT in


HandlePendingContexts()

you get the branch request twice, since one just happened on the server and then another one came in from the client, your PendingContext on the server gets duplicated on the list.
Everytime you have some pending Context you do this:
ablAbilityComponent.cpp : 891


ablAbilityComponent.cpp : 891

m_ActiveAbilityInstance = nullptr;

Which kills the currently playing ability

And then continue to play out the ability from the pending context - except this time the ability is on cooldown so it prints a warning to the log and doesn’t fire.

So in the end I assume you should only play the real branch on the server while on the client you should do some kinda fake branch as part of the “prediction” or not do it at all.