Recently I started to learn Gameplay Ability System and I have a problem regarding AttributeSet, Getters and Delegates that respond to Attribute changes. I have my Ability System Component created on MainCharacter which is Base Class for my future Characters I then derived CharacterPaladin class from it. I decided to create PlayerState when I will respond to all Attribute changes. However my game crashes when I use one of the AttributeSet Getters.
Crash LOG:
LoginId:f119695349758d2c727103ac0b900461
EpicAccountId:6f905fcc596f42b99f3aa89bdc239b69
Unhandled Exception: EXCEPTION_ACCESS_VIOLATION reading address 0x000000000000004c
UnrealEditor_GameplayAbilities
UnrealEditor_Projekt!AMainPlayerState::HealthChanged() [E:\WS_Ramen\Projekt\Source\Projekt\MainPlayerState.cpp:67]
UnrealEditor_Projekt!TBaseUObjectMethodDelegateInstance<0,AMainPlayerState,void __cdecl(FOnAttributeChangeData const &),FDefaultDelegateUserPolicy>::ExecuteIfSafe() [E:\UnrealEngine5\UE_5.0\Engine\Source\Runtime\Core\Public\Delegates\DelegateInstancesImpl.h:611]
UnrealEditor_GameplayAbilities
UnrealEditor_GameplayAbilities
UnrealEditor_GameplayAbilities
UnrealEditor_GameplayAbilities
UnrealEditor_GameplayAbilities
UnrealEditor_GameplayAbilities
UnrealEditor_GameplayAbilities
UnrealEditor_GameplayAbilities
UnrealEditor_GameplayAbilities
UnrealEditor_GameplayAbilities
UnrealEditor_GameplayAbilities
UnrealEditor_CoreUObject
UnrealEditor_CoreUObject
UnrealEditor_CoreUObject
UnrealEditor_CoreUObject
UnrealEditor_CoreUObject
UnrealEditor_CoreUObject
UnrealEditor_CoreUObject
UnrealEditor_CoreUObject
UnrealEditor_Engine
UnrealEditor_Engine
UnrealEditor_Engine
UnrealEditor_Engine
UnrealEditor_Engine
UnrealEditor_Engine
UnrealEditor_Engine
UnrealEditor_Engine
UnrealEditor_Core
UnrealEditor_Core
UnrealEditor_Core
UnrealEditor_Engine
UnrealEditor_Engine
UnrealEditor_Engine
UnrealEditor_Engine
UnrealEditor_UnrealEd
UnrealEditor_UnrealEd
UnrealEditor
UnrealEditor
UnrealEditor
UnrealEditor
UnrealEditor
UnrealEditor
kernel32
ntdll
These are my getters from AMainCharacter
AMainCharacter.h
virtual class UAbilitySystemComponent* GetAbilitySystemComponent() const override;
class UGASAttributeSet* GetPlayerAttributeSetBase() const;
AMainCharacter.cpp
UAbilitySystemComponent* AMainCharacter::GetAbilitySystemComponent() const
{
return AbilitySystemComponent;
}
UGASAttributeSet* AMainCharacter::GetPlayerAttributeSetBase() const
{
return Attributes;
}
And this is my MainPlayerState class
MainPlayerState.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerState.h"
#include "GameplayTagContainer.h"
#include "GASAttributeSet.h"
#include "MainPlayerState.generated.h"
/**
*
*/
UCLASS()
class PROJEKT_API AMainPlayerState : public APlayerState
{
GENERATED_BODY()
public:
AMainPlayerState();
UFUNCTION(BlueprintCallable, Category = "PlayerCharacter|MainPlayerState")
bool IsAlive() const;
//Getters for attributes
UFUNCTION(BlueprintCallable, Category = "GAS|BaseCharacter|Attributes")
float GetHealth() const;
UFUNCTION(BlueprintCallable, Category = "GAS|BaseCharacter|Attributes")
float GetMaxHealth() const;
UFUNCTION(BlueprintCallable, Category = "GAS|BaseCharacter|Attributes")
float GetResource() const;
UFUNCTION(BlueprintCallable, Category = "GAS|BaseCharacter|Attributes")
float GetMaxResource() const;
protected:
UPROPERTY()
class UGASAbilitySystemComponent* PlayerAbilitySystemComponent;
UPROPERTY()
class UGASAttributeSet* PlayerAttributeSetBase;
FGameplayTag DeadTag;
FDelegateHandle HealthChangedDelegateHandle;
FDelegateHandle MaxHealthChangedDelegateHandle;
FDelegateHandle ResourceChangedDelegateHandle;
FDelegateHandle MaxResourceChangedDelegateHandle;
virtual void BeginPlay() override;
// Attribute changed callbacks
virtual void HealthChanged(const FOnAttributeChangeData& Data);
virtual void MaxHealthChanged(const FOnAttributeChangeData& Data);
virtual void ResourceChanged(const FOnAttributeChangeData& Data);
virtual void MaxResourceChanged(const FOnAttributeChangeData& Data);
};
MainPlayerState.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "MainPlayerState.h"
#include "GASAbilitySystemComponent.h"
#include "MainCharacter.h"
#include "GASAttributeSet.h"
#include "UPlayerResourceWidget.h"
AMainPlayerState::AMainPlayerState()
{
DeadTag = FGameplayTag::RequestGameplayTag(FName("StateTag.Dead"));
}
bool AMainPlayerState::IsAlive() const
{
return GetHealth() > 0.0f;
}
float AMainPlayerState::GetHealth() const
{
AMainCharacter* Player = Cast<AMainCharacter>(GetPawn());
return Player->GetPlayerAttributeSetBase()->GetHealth();
}
float AMainPlayerState::GetMaxHealth() const
{
return PlayerAttributeSetBase->GetMaxHealth();
}
float AMainPlayerState::GetResource() const
{
return PlayerAttributeSetBase->GetResource();
}
float AMainPlayerState::GetMaxResource() const
{
return PlayerAttributeSetBase->GetMaxResource();
}
void AMainPlayerState::BeginPlay()
{
Super::BeginPlay();
GEngine->AddOnScreenDebugMessage(-1,2,FColor::Red,TEXT("Entered Begin play"));
AMainCharacter* Player = Cast<AMainCharacter>(GetPawn());
if(Player)
{
PlayerAbilitySystemComponent = Cast<UGASAbilitySystemComponent>(Player->GetAbilitySystemComponent());
PlayerAttributeSetBase = Player->GetPlayerAttributeSetBase();
if(PlayerAbilitySystemComponent)
{
GEngine->AddOnScreenDebugMessage(-1,2,FColor::Red,TEXT("Entered creating Delegets"));
HealthChangedDelegateHandle = PlayerAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(PlayerAttributeSetBase->GetHealthAttribute()).AddUObject(this,&AMainPlayerState::HealthChanged);
MaxHealthChangedDelegateHandle = PlayerAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(PlayerAttributeSetBase->GetMaxHealthAttribute()).AddUObject(this, &AMainPlayerState::MaxHealthChanged);
ResourceChangedDelegateHandle = PlayerAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(PlayerAttributeSetBase->GetResourceAttribute()).AddUObject(this, &AMainPlayerState::ResourceChanged);
MaxResourceChangedDelegateHandle = PlayerAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(PlayerAttributeSetBase->GetMaxResourceAttribute()).AddUObject(this, &AMainPlayerState::MaxResourceChanged);
}
}
}
void AMainPlayerState::HealthChanged(const FOnAttributeChangeData& Data)
{
float Health = Data.NewValue;
GEngine->AddOnScreenDebugMessage(-1,2,FColor::Red,FString::Printf(TEXT("Health: %f"),GetHealth()));
AMainCharacter* Player = Cast<AMainCharacter>(GetPawn());
UUPlayerResourceWidget* PlayerResourceWidget = Player->GetFloatingResourceWidget();
if(PlayerResourceWidget)
{
GEngine->AddOnScreenDebugMessage(-1,2,FColor::Red,TEXT("Entered Health Changed"));
PlayerResourceWidget->SetHeathPercentage(Health / GetMaxHealth());
}
/*
if(!IsAlive())
{
if(Player)
{
//TODO Call Die function here
GEngine->AddOnScreenDebugMessage(-1,4,FColor::Red,TEXT("PLAYER DIED"));
}
}
*/
}
void AMainPlayerState::MaxHealthChanged(const FOnAttributeChangeData& Data)
{
float MaxHealth = Data.NewValue;
AMainCharacter* Player = Cast<AMainCharacter>(GetPawn());
if(Player)
{
UUPlayerResourceWidget* PlayerResourceWidget = Player->GetFloatingResourceWidget();
if(PlayerResourceWidget)
{
PlayerResourceWidget->SetHeathPercentage(GetHealth() / MaxHealth);
}
}
}
void AMainPlayerState::ResourceChanged(const FOnAttributeChangeData& Data)
{
float Resource = Data.NewValue;
AMainCharacter* Player = Cast<AMainCharacter>(GetPawn());
if(Player)
{
UUPlayerResourceWidget* PlayerResourceWidget = Player->GetFloatingResourceWidget();
if(PlayerResourceWidget)
{
PlayerResourceWidget->SetManaPercentage(Resource / GetMaxResource());
}
}
}
void AMainPlayerState::MaxResourceChanged(const FOnAttributeChangeData& Data)
{
float MaxResource = Data.NewValue;
AMainCharacter* Player = Cast<AMainCharacter>(GetPawn());
if(Player)
{
UUPlayerResourceWidget* PlayerResourceWidget = Player->GetFloatingResourceWidget();
if(PlayerResourceWidget)
{
PlayerResourceWidget->SetManaPercentage(GetResource() / MaxResource);
}
}
}