cpp
// Copyright Druid Mechanics
#include "AbilitySystem/AbilityTasks/TargetDataUnderMouse.h"
#include "AbilitySystemComponent.h"
UTargetDataUnderMouse* UTargetDataUnderMouse::CreateTargetDataUnderMouse(UGameplayAbility* OwningAbility)
{
UTargetDataUnderMouse* MyObj = NewAbilityTask<UTargetDataUnderMouse>(OwningAbility);
return MyObj;
}
void UTargetDataUnderMouse::Activate()
{
const bool bIsLocallyControlled = Ability->GetCurrentActorInfo()->IsLocallyControlled();
if (bIsLocallyControlled)
{
SendMouseCursorData();
}
else
{
//TODO: We are on the server, so listen for target data.
}
}
void UTargetDataUnderMouse::SendMouseCursorData()
{
FScopedPredictionWindow ScopedPrediction(AbilitySystemComponent.Get());
APlayerController* PC = Ability->GetCurrentActorInfo()->PlayerController.Get();
FHitResult CursorHit;
PC->GetHitResultUnderCursor(ECC_Visibility, false, CursorHit);
FGameplayAbilityTargetDataHandle DataHandle;
FGameplayAbilityTargetData_SingleTargetHit* Data = new FGameplayAbilityTargetData_SingleTargetHit();
Data->HitResult = CursorHit;
DataHandle.Add(Data);
AbilitySystemComponent->ServerSetReplicatedTargetData(
GetAbilitySpecHandle(),
GetActivationPredictionKey(),
DataHandle,
FGameplayTag(),
AbilitySystemComponent->ScopedPredictionKey);
if (ShouldBroadcastAbilityTaskDelegates())
{
ValidData.Broadcast(DataHandle);
}
}
当前实现分析
TargetDataUnderMouse是一个Gameplay Ability Task类,用于获取鼠标光标下的目标数据并处理网络同步。当前实现具有以下特点:
核心功能
- 本地控制逻辑:当角色被本地控制时,直接获取鼠标下的命中结果并发送
- 远程控制逻辑:处理网络复制和回调
- 预测支持 :使用
FScopedPredictionWindow确保网络预测一致性 - 委托机制 :通过
ValidData委托返回有效的目标数据
关键代码分析
cpp
void UTargetDataUnderMouse::Activate()
{
const bool bIsLocallyControlled = Ability->GetCurrentActorInfo()->IsLocallyControlled();
if (bIsLocallyControlled)
{
SendMouseCursorData();
}
else
{
// 处理远程控制逻辑
}
}
cpp
void UTargetDataUnderMouse::SendMouseCursorData()
{
FScopedPredictionWindow ScopedPrediction(AbilitySystemComponent.Get());
APlayerController* PC = Ability->GetCurrentActorInfo()->PlayerController.Get();
FHitResult CursorHit;
PC->GetHitResultUnderCursor(ECC_Target, false, CursorHit);
FGameplayAbilityTargetDataHandle DataHandle;
FGameplayAbilityTargetData_SingleTargetHit* Data = new FGameplayAbilityTargetData_SingleTargetHit();
Data->HitResult = CursorHit;
DataHandle.Add(Data);
AbilitySystemComponent->ServerSetReplicatedTargetData(
GetAbilitySpecHandle(),
GetActivationPredictionKey(),
DataHandle,
FGameplayTag(),
AbilitySystemComponent->ScopedPredictionKey);
if (ShouldBroadcastAbilityTaskDelegates())
{
ValidData.Broadcast(DataHandle);
}
}
可能的修改方向及其原因和好处
1. 添加错误处理和验证
当前问题 :缺少对空指针的检查和错误处理,如AbilitySystemComponent、PlayerController等。
修改方向:
cpp
void UTargetDataUnderMouse::SendMouseCursorData()
{
if (!AbilitySystemComponent.IsValid() || !Ability.IsValid())
{
EndTask();
return;
}
FScopedPredictionWindow ScopedPrediction(AbilitySystemComponent.Get());
APlayerController* PC = Ability->GetCurrentActorInfo()->PlayerController.Get();
if (!PC)
{
EndTask();
return;
}
// 剩余代码...
}
好处:
- 提高代码稳定性,避免空指针崩溃
- 更好的错误处理,确保任务正确结束
2. 优化命中检测参数
当前问题 :硬编码使用ECC_Target通道,缺乏灵活性。
修改方向:
cpp
// 在头文件中添加
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Targeting")
TEnumAsByte<ECollisionChannel> TraceChannel = ECC_Target;
// 在SendMouseCursorData中使用
PC->GetHitResultUnderCursor(TraceChannel, false, CursorHit);
好处:
- 提高灵活性,允许蓝图配置不同的碰撞通道
- 支持多种目标类型的检测需求
3. 添加配置选项
当前问题:缺少对射线检测长度、是否忽略特定actor等配置。
修改方向:
cpp
// 在头文件中添加
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Targeting")
float TraceDistance = 5000.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Targeting")
bool bIgnoreSelf = true;
// 实现自定义射线检测逻辑,替代GetHitResultUnderCursor
好处:
- 提供更多配置选项,适应不同的游戏需求
- 提高代码复用性,减少重复实现
4. 改进网络同步
当前问题:网络同步逻辑较为简单,可能在某些情况下出现不一致。
修改方向:
cpp
// 添加网络验证和重试机制
void UTargetDataUnderMouse::OnTargetDataReplicatedCallback(const FGameplayAbilityTargetDataHandle& DataHandle, FGameplayTag ActivationTag)
{
// 验证数据有效性
if (DataHandle.IsValid())
{
AbilitySystemComponent->ConsumeClientReplicatedTargetData(GetAbilitySpecHandle(), GetActivationPredictionKey());
if (ShouldBroadcastAbilityTaskDelegates())
{
ValidData.Broadcast(DataHandle);
}
}
else
{
// 处理无效数据
EndTask();
}
}
好处:
- 提高网络同步的可靠性
- 更好地处理无效或损坏的数据
5. 支持多种目标数据类型
当前问题 :只支持FGameplayAbilityTargetData_SingleTargetHit类型。
修改方向:
cpp
// 支持多种目标数据类型
void UTargetDataUnderMouse::SendMouseCursorData()
{
// ... 现有代码 ...
// 根据配置创建不同类型的目标数据
if (bUseSingleTarget)
{
FGameplayAbilityTargetData_SingleTargetHit* Data = new FGameplayAbilityTargetData_SingleTargetHit();
Data->HitResult = CursorHit;
DataHandle.Add(Data);
}
else if (bUseLocation)
{
FGameplayAbilityTargetData_LocationInfo* Data = new FGameplayAbilityTargetData_LocationInfo();
Data->SetLocationInfoFromHitResult(CursorHit);
DataHandle.Add(Data);
}
// ... 剩余代码 ...
}
好处:
- 提高代码灵活性,支持不同类型的目标数据
- 适应不同的游戏机制需求
总结
当前的TargetDataUnderMouse实现已经具备了基本功能,但通过添加错误处理、优化配置选项、改进网络同步和支持多种目标数据类型,可以进一步提高其稳定性、灵活性和可扩展性。这些修改将使该类能够更好地适应不同的游戏需求,并提高代码的可维护性。
源代码
TargetDataUnderMouse.h
cpp
// 版权归陈超所有
#pragma once
#include "CoreMinimal.h"
#include "Abilities/Tasks/AbilityTask.h"
#include "TargetDataUnderMouse.generated.h"
/**
* 鼠标下目标数据委托,用于传递FGameplayAbilityTargetDataHandle类型的目标数据
*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMouseTargetDataSignature, FGameplayAbilityTargetDataHandle&, DataHandle);
/**
* 用于获取鼠标位置下目标数据的能力任务类
*/
UCLASS()
class CC_AURA_API UTargetDataUnderMouse : public UAbilityTask
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category="Ability|Tasks", meta = (DisplayName = "TargetDataUnderMouse", HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "true"))
static UTargetDataUnderMouse* CreateTargetDataUnderMouse(UGameplayAbility* OwningAbility);
UPROPERTY(BlueprintAssignable)
FMouseTargetDataSignature OnValidDataHandle; // 当获取到有效目标数据时触发的委托
protected:
// 碰撞通道,默认使用ECC_Target
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Targeting")
TEnumAsByte<ECollisionChannel> TraceChannel = ECollisionChannel::ECC_Visibility;
private:
virtual void Activate() override;
void SendMouseCursorData(); //发送鼠标下的目标数据
};
TargetDataUnderMouse.cpp
cpp
// 版权归陈超所有
#include "AbilitySystem/AbilityTasks/TargetDataUnderMouse.h"
#include "AbilitySystemComponent.h"
UTargetDataUnderMouse* UTargetDataUnderMouse::CreateTargetDataUnderMouse(UGameplayAbility* OwningAbility)
{
UTargetDataUnderMouse* MyObj = NewAbilityTask<UTargetDataUnderMouse>(OwningAbility);
return MyObj;
}
/**
* 激活任务,开始获取鼠标位置下的目标数据
*/
void UTargetDataUnderMouse::Activate()
{
const bool bIsLocallyControlled = Ability->GetCurrentActorInfo()->IsLocallyControlled();
if (bIsLocallyControlled)
{
SendMouseCursorData();
}
else
{
//TODO: We are on the server, so listen for target data.
}
}
/**
* 发送鼠标光标下的目标数据
* 该函数获取鼠标光标位置下的命中信息,并将其作为目标数据发送
*/
void UTargetDataUnderMouse::SendMouseCursorData()
{
if (!AbilitySystemComponent.IsValid() || !Ability)
{
EndTask();
return;
}
FScopedPredictionWindow ScopedPrediction(AbilitySystemComponent.Get());
APlayerController* PC = Ability->GetCurrentActorInfo()->PlayerController.Get();
if (!PC)
{
EndTask();
return;
}
FHitResult CursorHit;
PC->GetHitResultUnderCursor(TraceChannel, false, CursorHit);
FGameplayAbilityTargetDataHandle DataHandle;
FGameplayAbilityTargetData_SingleTargetHit* Data = new FGameplayAbilityTargetData_SingleTargetHit();
Data->HitResult = CursorHit;
DataHandle.Add(Data);
AbilitySystemComponent->ServerSetReplicatedTargetData(
GetAbilitySpecHandle(),
GetActivationPredictionKey(),
DataHandle,
FGameplayTag(),
AbilitySystemComponent->ScopedPredictionKey);
if (ShouldBroadcastAbilityTaskDelegates())
{
OnValidDataHandle.Broadcast(DataHandle);
}
}