I am using the GAS. I have a spell, which works, unless I add a cooldown effect. Then, the game immediately crashes when I try to activate this spell/ability. I pinpointed this to an invalid pointer inside of the member ActiveGameplayEffects of the responsible AbilitySystemComponent (ASC).
Somehow, this ASC is not created correctly I believe. This all happens in an item class (RTSItem) I created (+ inventory component).
Here is how I create the ASC:
ARTSItem::ARTSItem()
:AssociatedAbility(nullptr), Icon(nullptr)
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
bAlwaysRelevant = true;
this->SetRole(ROLE_Authority);
ASC = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("MyAwesomeComponent"));
ASC->SetIsReplicated(true);
ASC->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
Something must be up, because right here ASC->AbilityActorInfo is a nullptr and I have to create it explicitly. This does not happen in another class.
When I look inside the ASC with a debugger, I see that ActiveGameplayEffects has no owner set. Due to this, later on, when the ability is activated, during the handling of the cooldown effect, a call to ASC->ActivateGameplayEffects->Owner->GetWorld() fails, because Owner is a nullptr. This again, I just checked it, does not occur in the other class, which spawns the ASC component exactly as I posted it here.
Does anyone have an idea?
edit:
This is how I create the RTSItem objects
bool URTSInventoryComponent::AddItem(TSubclassOf<URTSSpell> ItemClass)
{
for (uint8 Index = 0; Index < ItemSlots.Num(); Index++)
{
if (ItemSlots[Index] == nullptr)
{
ARTSItem* NewItem = NewObject<ARTSItem>(this, ARTSItem::StaticClass());
NewItem->SetAbility(ItemClass, this->GetOwner());
ItemSlots[Index] = NewItem;
return true;
}
}
return false;
}
UCLASS(BlueprintType)
class CASTLEFIGHT_API ARTSItem : public AActor, public IAbilitySystemInterface, public ICommandGridInterface
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ARTSItem();
ARTSItem(TSubclassOf<URTSSpell> Spell);
UFUNCTION(BlueprintCallable)
bool ActivateAbility();
// Convenience member for some UI interfacing
UPROPERTY()
TSubclassOf<URTSSpell> AssociatedSpell;
UPROPERTY(BlueprintReadOnly)
UGameplayAbility* AssociatedAbility;
UPROPERTY()
TObjectPtr<UTexture2D> Icon;
UPROPERTY()
UAbilitySystemComponent* ASC;
void SetAbility(TSubclassOf<URTSSpell> Spell, AActor* OuterOwner);
UFUNCTION()
virtual UTexture2D* GetIcon() override;
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
FGameplayAbilitySpecHandle AbilitySpecHandle;
void SetupCombatComponent();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
It is really weird. I know for a fact that the GAS in general works. I have a builder character who can use spells through his own AbilitySystemComponent. The setup in the builder character seems to be identical to what I am doing in the RTSItem class, but some pointers are null the ASC of RTSItem which are not null for the builder character’s ASC.
Alright, I made it work. In the constructor, right after GetDefaultSubobject, I had to call
ASC->RegisterComponent();
so that OnRegister() is called, where all the pointers are set that were missing. While this solves the crashes - I can now use proper cooldowns - I have a bad feeling about this. It should not be necessary to call RegisterComponent(). What could be the issue here that makes me have to do this?