通过ArcInventory中的几个Task示例可以了解GAS中的AbilityTask 、TargetData 、Predicting 之间的相互协作
一、UArcInvAbilityTask_SimpleTarget
1.1、属性
FGameplayAbilityTargetDataHandle ServerTargetData;
简洁,仅有一个TargetDataHandle 负责数据传输
1.2、方法
重写了AbilityTask的函数,同时提供虚函数调用给子类重写
virtual void Activate() override;
virtual void OnTargetDataCallback(const FGameplayAbilityTargetDataHandle& Data, FGameplayTag ActivationTag);
virtual void OnTargetDataCancelled();
virtual FGameplayAbilityTargetDataHandle GenerateTargetHandle();
virtual void HandleTargetData(const FGameplayAbilityTargetDataHandle& Data);
virtual void HandleCancelled();
1.2.1、Activate
主要作用是将客户端的内容传送到服务端并一起执行之后的内容
FScopedPredictionWindow ScopedWindow(AbilitySystemComponent, IsPredictingClient());
FGameplayAbilityTargetDataHandle TargetDataHandle = GenerateTargetHandle();
if (IsPredictingClient())
{
FGameplayTag ActivationTag;
AbilitySystemComponent->CallServerSetReplicatedTargetData(GetAbilitySpecHandle(), GetActivationPredictionKey(),
TargetDataHandle, ActivationTag, AbilitySystemComponent->ScopedPredictionKey);
HandleTargetData(TargetDataHandle);
}
else
{
const bool bIsLocallyControlled = Ability->GetCurrentActorInfo()->IsLocallyControlled();
ServerTargetData = TargetDataHandle;
if (!IsLocallyControlled())
{
AbilitySystemComponent->AbilityTargetDataSetDelegate(GetAbilitySpecHandle(), GetActivationPredictionKey())
.AddUObject(this, &UArcInvAbilityTask_SimpleTarget::OnTargetDataCallback);
AbilitySystemComponent->AbilityTargetDataCancelledDelegate(GetAbilitySpecHandle(), GetActivationPredictionKey())
.AddUObject(this, &UArcInvAbilityTask_SimpleTarget::OnTargetDataCancelled);
AbilitySystemComponent->CallReplicatedTargetDataDelegatesIfSet(GetAbilitySpecHandle(), GetActivationPredictionKey());
SetWaitingOnRemotePlayerData();
}
else
{
FGameplayTag ActivationTag;
OnTargetDataCallback(TargetDataHandle, ActivationTag);
EndTask();
}
}
实现: 1、让子类重写GenerateTargetHandle ()函数生成需要的TargetData 2、客户端将该TargetData 通过Prediction 发送给服务器同时执行本地HandleTargetData ()逻辑,该逻辑由子类重写 3、服务器绑定数据接收回调并最终执行与客户端的相同逻辑
1.2.1、OnTargetDataCallback与OnTargetDataCancelled
作为服务器获取到TargetData 的回调函数使用,实际上也只是执行一遍客户端执行的HandleTargetData ()
void UArcInvAbilityTask_SimpleTarget::OnTargetDataCallback(const FGameplayAbilityTargetDataHandle& Data, FGameplayTag ActivationTag)
{
HandleTargetData(Data);
AbilitySystemComponent->ConsumeClientReplicatedTargetData(GetAbilitySpecHandle(), GetActivationPredictionKey());
EndTask();
}
void UArcInvAbilityTask_SimpleTarget::OnTargetDataCancelled()
{
HandleCancelled();
EndTask();
}
1.3、总结
虽然实现简单,但同时使用了Predicting 作为网络预测、AbilityTask 作为任务发起、TargetDataHandle 作为网络传输的数据,麻雀虽小五脏俱全。
解决的问题 本地操作的数据需要在GA中同步到服务器一起执行 保证服务器与客户端执行GA时使用的数据是一样的 大体方案:
1、首先让子类生成不同类型的TargetData 2、通过预测窗口将数据发送到服务器并让本地预测执行HandleTargetData 3、服务器接收到数据后也开始执行HandleTargetData
二、UArcAbilityTask_SwitchInventory
2.1、属性
public:
UPROPERTY(BlueprintAssignable)
FArcSwitchItemsRecieved OnSlotsRecieved;
UPROPERTY(BlueprintAssignable)
FArcItemSwitchCancelled OnSlotsCancelled;
protected:
FArcInventoryItemSlotReference FromSlot;
FArcInventoryItemSlotReference ToSlot;
2.1.2、OnSlotsRecieved与OnSlotsCancelled
作为Task的输出Pin,其反射定义为:
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FArcSwitchItemsRecieved, const FArcInventoryItemSlotReference&, FromSlot, const FArcInventoryItemSlotReference&, ToSlot);
用于返回切换库存时的两个库存引用
2.1.2、FromSlot与ToSlot
作为外界传入Task的两个Slot,代表了SwitchSlot的两个参数。也是客户端需要发送给服务端的真实数据
2.2、接口
UFUNCTION(BlueprintCallable, meta = (HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "true", HideSpawnParms = "Instigator"), Category = "Ability|Tasks")
static UArcAbilityTask_SwitchInventory* SwitchInventorySlots(UGameplayAbility* OwningAbility, const FArcInventoryItemSlotReference& FromSlot, const FArcInventoryItemSlotReference& ToSlot);
virtual FGameplayAbilityTargetDataHandle GenerateTargetHandle() override;
virtual void HandleTargetData(const FGameplayAbilityTargetDataHandle& Data) override;
virtual void HandleCancelled() override;
2.2.1、SwitchInventorySlots()
作用: 蓝图调用Task的接口
UArcAbilityTask_SwitchInventory* Task = NewAbilityTask<UArcAbilityTask_SwitchInventory>(OwningAbility);
Task->FromSlot = FromSlot;
Task->ToSlot = ToSlot;
return Task;
实现: 将传入的两个ItemSlotReference 保存,为客户端发送给服务器做准备
2.2.2、GenerateTargetHandle()
作用: 重写了父类函数,来实现自己的TargetData
FGameplayAbilityTargetData_ItemSwitch* ItemSwitchData = new FGameplayAbilityTargetData_ItemSwitch();
ItemSwitchData->FromSlot = FromSlot;
ItemSwitchData->ToSlot = ToSlot;
return FGameplayAbilityTargetDataHandle(ItemSwitchData);
实现: 创建用于交换ItemSlot 的TargetData 并填入相关数据,等待发送给服务器
2.2.3、HandleTargetData()与
作用: 处理TargetData中的数据
void UArcAbilityTask_SwitchInventory::HandleTargetData(const FGameplayAbilityTargetDataHandle& Data)
{
const FGameplayAbilityTargetData_ItemSwitch* SwitchData = static_cast<const FGameplayAbilityTargetData_ItemSwitch*>(Data.Get(0));
if (SwitchData != nullptr){
OnSlotsRecieved.Broadcast(SwitchData->FromSlot, SwitchData->ToSlot);}
else{
OnSlotsCancelled.Broadcast();}
}
void UArcAbilityTask_SwitchInventory::HandleCancelled()
{
OnSlotsCancelled.Broadcast();
}
实现: 主要是通过两个Pin将TargetData 中的数据发送出去,当有数据的时候走OnSlotsRecieved 端口,没有数据时走OnSlotsCancelled 如果没有收到的话则通过OnSlotsCancelled 端口返回
2.2.4、FGameplayAbilityTargetData_ItemSwitch
一个存储FromSlot 和ToSlot 的数据结构
USTRUCT(BlueprintType)
struct FGameplayAbilityTargetData_ItemSwitch : public FGameplayAbilityTargetData
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY()
FArcInventoryItemSlotReference FromSlot;
UPROPERTY()
FArcInventoryItemSlotReference ToSlot;
virtual UScriptStruct* GetScriptStruct() const override{
return FGameplayAbilityTargetData_ItemSwitch::StaticStruct();
}
virtual FString ToString() const override{
return TEXT("FGameplayAbilityTargetData_ItemSwitch");
}
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);
};
template<>
struct TStructOpsTypeTraits<FGameplayAbilityTargetData_ItemSwitch> : public TStructOpsTypeTraitsBase2<FGameplayAbilityTargetData_ItemSwitch>
{
enum{
WithNetSerializer = true
};
};
需要注意的点:
GetScriptStruct ():需要重写为自己类的ToString() :与GetScriptStruct ()配套,用于DebugNetSerialize() :用于手动序列化,需要同时配套TStructOpsTypeTraits<FGameplayAbilityTargetData_ItemSwitch> TStructOpsTypeTraits<FGameplayAbilityTargetData_ItemSwitch> :给结构体添加WithNetSerializer 宏,告知UBT该结构体实现了NetSerialize ()函数,可以调用
2.3、总结
目的: 实现从客户端发送FromSlot 和ToSlot 给服务器并一起执行之后的GA逻辑 方案: 在父类的客户端服务器发送方案的基础下: 添加了外部接口SwitchInventorySlots ()供外部调用该Task 重写GenerateTargetHandle ()以及HandleTargetData ()来进行数据的发送和处理
|