GAS学习笔记(二)

本文详细介绍了虚幻引擎中Meteor技能学习时遇到的WaitTargetData节点和TargetActor的使用,包括它们在获取场景信息、数据传递和调试工具制作中的关键作用,以及如何通过Delegate实现异步操作控制流程。
摘要由CSDN通过智能技术生成

记录下学习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来进行传递。

  1. 创建TargetData,并且填充数据。
  2. 创建FGameplayAbilityTargetDataHandle对象(也可以使用带参的构造函数直接构建),并且调用Add()添加上面创建的TargetData。
  3. 进行传递。
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);
}

参考文章

虚幻引擎游戏技能系统文档-CSDN博客

UE4 GAS学习笔记(1)TargetActor 与 WaitTargetData - 哔哩哔哩

GAS的TargetData与TargetActor相关机制 - 知乎

Ability-示例:交互功能 - 知乎

  • 9
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值