I use FGameplayAbilityTargetData_SingleTargetHit and pass in hit results from the GetHitResultUnderCursor. Then in the ability, I use the hit result data to SetControlRotation on the character then play a montage. However, the montage plays first and then the rotation happens
FHitResult Hit;
if(GetHitResultUnderCursor(ECC_Visibility, false, Hit))
{ ACharacter* Character = GetCharacter();
FGameplayAbilityTargetData_SingleTargetHit *HitData = new FGameplayAbilityTargetData_SingleTargetHit(Hit);
FGameplayAbilityTargetDataHandle Data(HitData);
FGameplayEventData Payload;
Payload.Instigator = Character;
Payload.Target = Character;
Payload.TargetData = Data;
const FGameplayTag Tag = FGameplayTag::RequestGameplayTag(FName("GameplayEvent.Trigger.Attack"));
UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(Character, Tag, Payload);
}
const FVector_NetQuantize Location = TriggerEventData->TargetData.Get(0)->GetHitResult()->Location;
FRotator Rotation = UKismetMathLibrary::FindLookAtRotation(GetActorInfo().OwnerActor->GetActorLocation(), Location);
Rotation.Roll = 0.0f;
Rotation.Pitch = 0.0f;
ACharacter* Character = Cast<ACharacter>(GetActorInfo().OwnerActor);
Character->GetController()->SetControlRotation(Rotation);
UAbilityTask_PlayMontageWaitEvent* Task = UAbilityTask_PlayMontageWaitEvent::PlayMontageAndWaitForEvent(this, NAME_None, MontageToPlay, FGameplayTagContainer(), 1.0f, NAME_None, true, 1.0f);
Task->OnBlendOut.AddDynamic(this, &UGameplayAbility_Attack::OnCompleted);
Task->OnCompleted.AddDynamic(this, &UGameplayAbility_Attack::OnCompleted);
Task->OnInterrupted.AddDynamic(this, &UGameplayAbility_Attack::OnCancelled);
Task->OnCancelled.AddDynamic(this, &UGameplayAbility_Attack::OnCancelled);
Task->ReadyForActivation();
I’m afraid to put any artificial delay because I want the montage(an attacking animation) to play instantly the player rotates.