虚幻引擎5 GAS开发俯视角RPG游戏 P07-19.发送鼠标光标数据

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类,用于获取鼠标光标下的目标数据并处理网络同步。当前实现具有以下特点:

核心功能

  1. 本地控制逻辑:当角色被本地控制时,直接获取鼠标下的命中结果并发送
  2. 远程控制逻辑:处理网络复制和回调
  3. 预测支持 :使用FScopedPredictionWindow确保网络预测一致性
  4. 委托机制 :通过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. 添加错误处理和验证

当前问题 :缺少对空指针的检查和错误处理,如AbilitySystemComponentPlayerController等。

修改方向

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);
	}
}
相关推荐
日更嵌入式的打工仔2 小时前
Ethercat COE 笔记
网络·笔记·ethercat
玖剹2 小时前
哈希表相关题目
数据结构·c++·算法·leetcode·哈希算法·散列表
星火开发设计2 小时前
Python冒泡排序详解:从原理到代码实现与优化
开发语言·笔记·python·开源·排序算法·课程设计
小智RE0-走在路上2 小时前
Python学习笔记(9) --文件操作
笔记·python·学习
暗然而日章2 小时前
C++基础:Stanford CS106L学习笔记 14 类型安全 & `std::optional`
c++·笔记·学习
L_09072 小时前
【C++】高阶数据结构 -- 二叉搜索树(BST)
数据结构·c++
WongLeer2 小时前
Redis 学习笔记
redis·笔记·学习·redis缓存·redis发布订阅
大筒木老辈子2 小时前
C++笔记---并发支持库(future)
java·c++·笔记
PyGata3 小时前
CMake学习笔记(二):CMake拷贝文件夹
c++·笔记·学习