记录下学习Meteor技能的一些心得。主要是WaitTargetData结点和TargetActor的一些研究
TargetActor是GAS用于获取场景中物体、空间、位移等数据的机制,同时也可以用于制作可视化debug工具。所以非常有必要掌握它。
一般流程为:使用WaitTargetData_AbilityTask生成TargetActor,之后通过TargetActor的内部函数或者射线获取场景信息,最后通过委托传递携带这些信息构建的FGameplayAbilityTargetDataHandle。
1 WaitTargetData
在选择好TargetActor的类的时候,会自动添相关的引脚
所以WaitTargetData内部是如何实现让我们可以等待用户操作后再执行后面的逻辑的呢 ?其实就是通过万能的Delegate,让我们一梳理下。
1.1 生成TargetActor
执行WaitTargetData结点的时候,会自动执行BeginSpawningActor和FinishSpawningActor函数去生成TargetActor和相关的初始化(最重要的代理的绑定)
UAbilityTask_WaitTargetData::BeginSpawningActor
bool UAbilityTask_WaitTargetData::BeginSpawningActor(UGameplayAbility* OwningAbility, TSubclassOf<AGameplayAbilityTargetActor> InTargetClass, AGameplayAbilityTargetActor*& SpawnedActor)
{
SpawnedActor = nullptr;
if (Ability)
{
if (ShouldSpawnTargetActor())
{
UClass* Class = *InTargetClass;
if (Class != nullptr)
{
if (UWorld* World = GEngine->GetWorldFromContextObject(OwningAbility, EGetWorldErrorMode::LogAndReturnNull))
{
SpawnedActor = World->SpawnActorDeferred<AGameplayAbilityTargetActor>(Class, FTransform::Identity, nullptr, nullptr, ESpawnActorCollisionHandlingMethod::AlwaysSpawn);
}
}
if (SpawnedActor)
{
TargetActor = SpawnedActor;
InitializeTargetActor(SpawnedActor);
}
}
RegisterTargetDataCallbacks();
}
return (SpawnedActor != nullptr);
}
UAbilityTask_WaitTargetData::InitializeTargetActor
void UAbilityTask_WaitTargetData::InitializeTargetActor(AGameplayAbilityTargetActor* SpawnedActor) const
{
check(SpawnedActor);
check(Ability);
SpawnedActor->PrimaryPC = Ability->GetCurrentActorInfo()->PlayerController.Get();
// If we spawned the target actor, always register the callbacks for when the data is ready.
SpawnedActor->TargetDataReadyDelegate.AddUObject(const_cast<UAbilityTask_WaitTargetData*>(this), &UAbilityTask_WaitTargetData::OnTargetDataReadyCallback);
SpawnedActor->CanceledDelegate.AddUObject(const_cast<UAbilityTask_WaitTargetData*>(this), &UAbilityTask_WaitTargetData::OnTargetDataCancelledCallback);
}
UAbilityTask_WaitTargetData::RegisterTargetDataCallbacks
void UAbilityTask_WaitTargetData::RegisterTargetDataCallbacks()
{
............................
// else register callback for when it does get here.
if (!bIsLocallyControlled)
{
..............................
ASC->AbilityTargetDataSetDelegate(SpecHandle, ActivationPredictionKey ).AddUObject(this, &UAbilityTask_WaitTargetData::OnTargetDataReplicatedCallback);
ASC->AbilityTargetDataCancelledDelegate(SpecHandle, ActivationPredictionKey ).AddUObject(this, &UAbilityTask_WaitTargetData::OnTargetDataReplicatedCancelledCallback);
ASC->CallReplicatedTargetDataDelegatesIfSet(SpecHandle, ActivationPredictionKey );
........................
}
}
}
UAbilityTask_WaitTargetData::FinishSpawningActor
void UAbilityTask_WaitTargetData::FinishSpawningActor(UGameplayAbility* OwningAbility, AGameplayAbilityTargetActor* SpawnedActor)
{
UAbilitySystemComponent* ASC = AbilitySystemComponent.Get();
if (ASC && IsValid(SpawnedActor))
{
check(TargetActor == SpawnedActor);
const FTransform SpawnTransform = ASC->GetOwner()->GetTransform();
SpawnedActor->FinishSpawning(SpawnTransform);
FinalizeTargetActor(SpawnedActor);
}
}
UAbilityTask_WaitTargetData::FinalizeTargetActor
void UAbilityTask_WaitTargetData::FinalizeTargetActor(AGameplayAbilityTargetActor* SpawnedActor) const
{
check(SpawnedActor);
check(Ability);
if (UAbilitySystemComponent* ASC = AbilitySystemComponent.Get())
{
// User ability activation is inhibited while this is active
ASC->SpawnedTargetActors.Push(SpawnedActor);
}
SpawnedActor->StartTargeting(Ability);
if (SpawnedActor->ShouldProduceTargetData())
{
// If instant confirm, then stop targeting immediately.
// Note this is kind of bad: we should be able to just call a static func on the CDO to do this.
// But then we wouldn't get to set ExposeOnSpawnParameters.
if (ConfirmationType == EGameplayTargetingConfirmation::Instant)
{
SpawnedActor->ConfirmTargeting();
}
else if (ConfirmationType == EGameplayTargetingConfirmation::UserConfirmed)
{
// Bind to the Cancel/Confirm Delegates (called from local confirm or from repped confirm)
SpawnedActor->BindToConfirmCancelInputs();
}
}
}
可以看到,在FinalizeTargetActor中会对ConfirmationType中Instant和Userconfirmed进行相应的处理,如果是Instant,会立即调用TargetActor中的ConfirmTargeting方法,如果是Userconfirmed,会将绑定相关的Delegates,比如鼠标左键作为确认键
AGameplayAbilityTargetActor::BindToConfirmCancelInputs
void AGameplayAbilityTargetActor::BindToConfirmCancelInputs()
{
.................
if (ASC)
{
if (Info->IsLocallyControlled())
{
// We have to wait for the callback from the AbilitySystemComponent. Which will always be instigated locally
ASC->GenericLocalConfirmCallbacks.AddDynamic(this, &AGameplayAbilityTargetActor::ConfirmTargeting); // Tell me if the confirm input is pressed
ASC->GenericLocalCancelCallbacks.AddDynamic(this, &AGameplayAbilityTargetActor::CancelTargeting); // Tell me if the cancel input is pressed
// Save off which ASC we bound so that we can error check that we're removing them later
GenericDelegateBoundASC = ASC;
}
else
{
.......................
GenericConfirmHandle = ASC->AbilityReplicatedEventDelegate(EAbilityGenericReplicatedEvent::GenericConfirm, Handle, PredKey ).AddUObject(this, &AGameplayAbilityTargetActor::ConfirmTargeting);
GenericCancelHandle = ASC->AbilityReplicatedEventDelegate(EAbilityGenericReplicatedEvent::GenericCancel, Handle, PredKey ).AddUObject(this, &AGameplayAbilityTargetActor::CancelTargeting);
.......................
}
}
}
1.2 流程
经过上面的初始化,这时候就只需等待数据到来,那当我们输入Confirm键的时候,就会一步一步通过广播前面初始化的一系列的Delegate,进行相依阶段的逻辑处理,大致流程如下
2 TargetActor
TargetActor可以理解为一个场景信息探测器,用来获取场景中数据。它主要用于存储目标数据(一般是TArray >)、FHitResult。前面说了如何初始化,那下面讲讲它大概是如何工作的。以AGameplayAbilityTargetActor_GroundTrace地面追踪为例。
2.1 检测
GroundTrace的检测实现在PerformTrace,这个函数会在Tick中调用,并且在ConfirmTargetingAndContinue中也会执行一次用于构造TargetData。
1.首先计算出检测的起点和终点
2. 进行一系列的射线检测
3.生成FHitResult
2.2 生成TargetData
TargetData也就是FGameplayAbilityTargetData是用于通过网络传输定位数据的通用结构体。
GAS不会直接传递数据,而是会借助FGameplayAbilityTargetDataHandle来进行传递。
- 创建TargetData,并且填充数据。
- 创建FGameplayAbilityTargetDataHandle对象(也可以使用带参的构造函数直接构建),并且调用Add()添加上面创建的TargetData。
- 进行传递。
void AGameplayAbilityTargetActor_Trace::ConfirmTargetingAndContinue()
{
check(ShouldProduceTargetData());
if (SourceActor)
{
bDebug = false;
FGameplayAbilityTargetDataHandle Handle = MakeTargetData(PerformTrace(SourceActor));
TargetDataReadyDelegate.Broadcast(Handle);
}
}
FGameplayAbilityTargetDataHandle AGameplayAbilityTargetActor_Trace::MakeTargetData(const FHitResult& HitResult) const
{
/** Note: This will be cleaned up by the FGameplayAbilityTargetDataHandle (via an internal TSharedPtr) */
return StartLocation.MakeTargetDataHandleFromHitResult(OwningAbility, HitResult);
}
参考文章
UE4 GAS学习笔记(1)TargetActor 与 WaitTargetData - 哔哩哔哩