How to Listen to Delegate in a Gameplay Ability C++ Class UE5.4

Hi All,
I am currently working on making a ComboAttack blueprint in C++, however it seems to not be able to listen to the delegate in my player character for when anim notifys are hit in the montage played by the ability.
The Delegate is being fired correctly, and it is being broadcasted however for whatever reason the ability function for the delegate is not firing.
Any help is appreciated as I have spent 5 hours on this today!

Ability Class:

// Fill out your copyright notice in the Description page of Project Settings.


#include "CharacterAttackCombo.h"
#include "Abilities/Tasks/AbilityTask_PlayMontageAndWait.h"
#include "Abilities/Tasks/AbilityTask_WaitGameplayEvent.h"
#include "AbilitySystemComponent.h"
#include "Animation/AnimInstance.h"
#include "UObject/ConstructorHelpers.h"
#include "Animation/AnimMontage.h"
#include "PlayerCharacter.h"
#include "GameFramework/Actor.h"
#include "GameFramework/Character.h"

UCharacterAttackCombo::UCharacterAttackCombo()
{
	// Ability properties initialization
	static ConstructorHelpers::FObjectFinder<UAnimMontage> Montage(TEXT("/Game/Soulsbourne/Animations/Gorka/Combat/MONT_SwordAttackCombo.MONT_SwordAttackCombo"));
	if (Montage.Succeeded())
	{
		MontageToPlay = Montage.Object;
	}
}

void UCharacterAttackCombo::InitializeAbilityTags(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, UAbilitySystemComponent* ASC) {
	AbilityTags.AddTag(FGameplayTag::RequestGameplayTag(FName("Character.AttackCombo")));

	CancelAbilitiesWithTag.AddTag(FGameplayTag::RequestGameplayTag(FName("Player.Abilities.Block")));

	BlockAbilitiesWithTag.AddTag(FGameplayTag::RequestGameplayTag(FName("Player.Abilities.Jump")));
	BlockAbilitiesWithTag.AddTag(FGameplayTag::RequestGameplayTag(FName("Player.Abilities.Block")));
	BlockAbilitiesWithTag.AddTag(FGameplayTag::RequestGameplayTag(FName("Player.Abilities.Roll")));

	ActivationBlockedTags.AddTag(FGameplayTag::RequestGameplayTag(FName("Player.Abilities.Roll")));
	ActivationBlockedTags.AddTag(FGameplayTag::RequestGameplayTag(FName("Player.IsAirborne")));
	ActivationBlockedTags.AddTag(FGameplayTag::RequestGameplayTag(FName("Character.Attack")));
	ActivationBlockedTags.AddTag(FGameplayTag::RequestGameplayTag(FName("Character.AttackCombo")));

	ActivationOwnedTags.AddTag(FGameplayTag::RequestGameplayTag(FName("Character.AttackCombo")));

	ASC->AddLooseGameplayTags(ActivationOwnedTags);
	if (ASC->AreAbilityTagsBlocked(ActivationBlockedTags))
	{
		EndAbility(Handle, ActorInfo, ActivationInfo, true, false);
		return;
	}
	ASC->BlockAbilitiesWithTags(BlockAbilitiesWithTag);
	ASC->CancelAbilities(&CancelAbilitiesWithTag, nullptr, nullptr);
}

void UCharacterAttackCombo::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
	Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);
	APlayerCharacter* PlayerCharacter = Cast<APlayerCharacter>(ActorInfo->OwnerActor);
	if (PlayerCharacter)
	{
		// Bind to the player character's event dispatcher
		PlayerCharacter->OnMontageNotify.AddDynamic(this, &UCharacterAttackCombo::OnMontageNotifyStart);
	}
	UAbilitySystemComponent* ASC = ActorInfo->AbilitySystemComponent.Get();
	if (HasAuthorityOrPredictionKey(ActorInfo, &ActivationInfo) && ASC)
	{
		InitializeAbilityTags(Handle, ActorInfo, ActivationInfo, ASC);

		USkeletalMeshComponent* SkeletalMeshComponent = nullptr;
		UAnimInstance* AnimInstance = nullptr;

		AActor* AvatarActor = ActorInfo->AvatarActor.Get();
		if (AvatarActor)
		{
			SkeletalMeshComponent = AvatarActor->FindComponentByClass<USkeletalMeshComponent>();
			AnimInstance = SkeletalMeshComponent->GetAnimInstance();
		}


		if (SkeletalMeshComponent && AnimInstance)
		{
			// Perform your logic here
			AnimInstance->Montage_Play(MontageToPlay);
		}

		PerformComboAttack(Handle, ActorInfo, ASC);
		EndAbility(Handle, ActorInfo, ActivationInfo, true, false);

		ASC->RemoveLooseGameplayTags(ActivationOwnedTags);
		ASC->UnBlockAbilitiesWithTags(BlockAbilitiesWithTag);
	}
}

void UCharacterAttackCombo::OnMontageNotifyStart(FName NotifyName, const FBranchingPointNotifyPayload& BranchingPointNotifyPayload) {
	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("LALALALALLAL"));
	UE_LOG(LogTemp, Log, TEXT("Binding OnPlayMontageNotifyBegin delegate"))
		UE_LOG(LogTemp, Log, TEXT("Notify Begin: %s"), *NotifyName.ToString());
}
void UCharacterAttackCombo::PerformComboAttack(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, UAbilitySystemComponent* ASC)
{
	// Logic for performing the combo attack
	// This could involve playing animations, applying damage, etc.
	USkeletalMeshComponent* SkeletalMeshComponent = nullptr;
	UAnimInstance* AnimInstance = nullptr;

	AActor* AvatarActor = ActorInfo->AvatarActor.Get();
	if (AvatarActor)
	{

	}

	if (SkeletalMeshComponent && AnimInstance)
	{
		// Perform your logic here
		//AnimInstance->Montage_Play(MontageToPlay);

	}
}

Character Delegate Declaration:

DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FMontageNotifyDelegate, FName, NotifyName, const FBranchingPointNotifyPayload&, BranchingPointPayload);
UCLASS()
class SOULSBORNE_API APlayerCharacter : public ACharacter, public IAbilitySystemInterface, public IProgressBarInterface
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	APlayerCharacter();
	UFUNCTION()
	void OnMontageNotifyStart(FName NotifyName, const FBranchingPointNotifyPayload& BranchingPointPayload);
	UPROPERTY(BlueprintAssignable, Category = "Animation")
	FMontageNotifyDelegate OnMontageNotify;

Character Class Sending out and Binding the delegate:


/** Called when the game starts or when spawned */
void APlayerCharacter::BeginPlay()
{
	Super::BeginPlay();

	if (APlayerController* PlayerController = Cast<APlayerController>(GetController()))
	{
		PlayerController->SetInputMode(FInputModeGameOnly());
		if (HUDWidget)
		{
			PlayerHUD = CreateWidget<UUserWidget>(PlayerController, HUDWidget, "HUD");
			check(PlayerHUD);
			PlayerHUD->AddToPlayerScreen();

		}
		//Static call for now
		FName rightSocketName = "righthandSocket";
		AttatchEquipment(LHandArmament, rightSocketName);

		if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
		{
			Subsystem->AddMappingContext(MyInputMappingContext, 1);
		}


		this->GetMesh()->GetAnimInstance()->OnPlayMontageNotifyEnd.AddDynamic(this, &APlayerCharacter::OnMontageNotifyStart);
	}
}
void APlayerCharacter::OnMontageNotifyStart(FName NotifyName, const FBranchingPointNotifyPayload& BranchingPointNotifyPayload) {
	//UE_LOG(LogTemp, Log, TEXT("Binding OnPlayMontageNotifyBegin delegate"))
	UE_LOG(LogTemp, Log, TEXT("Notify Begin: %s"), *NotifyName.ToString());
	OnMontageNotify.Broadcast(NotifyName, BranchingPointNotifyPayload);
}

might not be the issue but be careful binding on BeginPlay as if the controller ‘Begins’ before the character the character doesn’t exist yet to bind too

Ah I see, thank you for the tip!
I do not think that is the answer to the original question, as the delegate is bound and broadcasted correctly in the PlayerCharacter (I Checked with UE_Logs), but that is an interesting thought for the player character!

I am starting to think it is something to do with how a Gameplay ability is created and ends. But I found a workaround creating the attack directly in the player class for now.

I found it! My sniffer was right, it was a problem with how it ends.
The Activate Ability does not wait for the montage to play, so it calls the end ability right after executing. Silly me :slight_smile:

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.