当前位置: 首页 > news >正文

UE TargetingSystem插件介绍

最近看代码的时候,偶然看到了UE添加了一个新的插件TargetingSystem,大致浏览了代码后,我认为这可以比较方便的作为一个目标选择框架的底层代码。

一、引言 — 为什么需要 TargetingSystem

在游戏开发中,"在空间中找到一组目标"是一个极其高频的需求,比如动作游戏角色攻击的打击判定。如果基于GAS开发的话,很多项目一开始都会使用TargetActor用于碰撞检测/判定。但是TargetActor作为Actor,它的创建销毁的开销比较大,随着项目的进行,往往都会优化掉TargetActor,然后基于引擎的碰撞检测逻辑如BoxTraceMulti去实现打击判定的碰撞检测逻辑。

其它系统,也有类似的"在空间中找到一组目标"需求,如:AI 需要选取最优攻击目标、拾取系统需要找到附近的物品、自动锁定需要筛选视野内的敌人……

传统做法是在每个系统中各自编写碰撞查询逻辑,然后加上各自的过滤条件,导致大量重复代码,且难以统一配置和调试。最近我翻代码的时候,发现UE5.2引入了一个新插件TargetingSystem,翻看代码过后,我认为它对"在空间中找到一组目标"这一目的,可以作为一个通用的数据驱动的目标查询框架。

它的核心思想是:将"寻找目标"这一行为抽象为一条可配置的任务管线(Task Pipeline),由 Selection(选取)→ Filter(过滤)→ Sort(排序) 三类任务组成,通过数据资产(DataAsset)驱动,无需编写硬编码的碰撞查询逻辑。

同时TargetingSystem还支持了异步的查询,对于性能优化也很有利。

二、整体架构概览

TargetingSystem 的设计大致如下:

  • 通过 UTargetingPreset 数据资产作为一个独立的目标检测规则,如角色的攻击判定。配置检测流程TargetingTasks

  • TargetingTask为具体的目标选择逻辑,比如Overlap,Trace等,它大致可以分为选择,过滤,排序三种任务。可以自己实现对应的逻辑适配项目。比如玩家释放一个冲刺技能,目标是攻击距离最近的敌人。那对应的TargetingTask如下:

    1. SelectionTask选择一定范围内的actors

    2. FilterTask选择敌人的怪物作为目标,过滤掉其余的actors

    3. SortTask对目标候选进行排序,距离越近越优先。

  • 目标检测的行为通过创建TargetingHandle在全局的DataStore中进行维护,不涉及Spawn任何的Actor

  • 创建的TargetingHandle由TargetingSubsystem进行管理,负责调度执行具体的目标选择逻辑。支持同步和异步两种执行模式。

  • 目标选择逻辑执行完后,通过Delegate回调给发起者,执行后续的逻辑。

三、如何应用

有了TargetSystem的架构设计以后,我这里打算找一个具体的应用场景,作为使用的示例。我这里打算使用Lyra,将它的近战攻击GA_Melee改为使用TargetingSystem执行

首先打开对应的蓝图文件,可以看到近战攻击的流程如下:

  1. 播放攻击动画

  2. 碰撞检测找到目标

  3. 对目标施加效果

TargetingSystem可以替换的是上述的第二步,接下来分析一下近战攻击寻找目标的流程:

  • 通过胶囊体碰撞检测,获取角色正前方的目标

  • 通过队伍系统,检测是否为敌人

  • 过滤掉墙后的敌人

按照TargetingSystem的架构设计,我们可以通过TargetingTask来复刻上述的功能。

  1. TargetingSelectionTask_Melee,使用胶囊体碰撞检测角色身前的目标,直接使用CapsuleTraceMultiForObjects接口即可

  2. TargetingFilterTask_Team,比较碰撞目标和角色的队伍,需要为敌对的

  3. TargetingFilterTask_Block,目标和角色之间,不存在墙体遮挡

具体的逻辑很简单,就不写了,相关代码我贴到最后面。

有了Task以后,然后需要配置具体的TargetingPreset。创建TargetingPreset的DataAsset,然后配置上对应的Task。

接下来打开GA_Melee,使用AbilityTask-PerformTargetingRequest去替代原有的逻辑,InTargetingRequest配置前面创建的DA_TP_Melee。

逻辑完成,运行游戏按C键普攻测试即可。

四、测试代码

UTargetingSelectionTask_Melee

void UTargetingSelectionTask_Melee::Execute(const FTargetingRequestHandle& TargetingHandle) const { FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle); if (!SourceContext) return; AActor* SourceActor = SourceContext->SourceActor; if (!SourceActor) return; UWorld* World = GetSourceContextWorld(TargetingHandle); if (!World) return; FVector Direction = SourceActor->GetActorForwardVector(); FVector Start = SourceActor->GetActorLocation(); FVector End = Start + Direction * TraceLength; TArray<FHitResult> OutHits; UKismetSystemLibrary::CapsuleTraceMultiForObjects(World, Start, End, Radius, HalfHeight, {ObjectType}, false, {SourceActor}, EDrawDebugTrace::None, OutHits, true); ProcessHitResults(TargetingHandle, OutHits); SetTaskAsyncState(TargetingHandle, ETargetingTaskAsyncState::Completed); } void UTargetingSelectionTask_Melee::ProcessHitResults(const FTargetingRequestHandle& TargetingHandle, const TArray<FHitResult>& Hits) const { if (TargetingHandle.IsValid() && Hits.Num() > 0) { FTargetingDefaultResultsSet& TargetingResults = FTargetingDefaultResultsSet::FindOrAdd(TargetingHandle); for (const FHitResult& HitResult : Hits) { if (!HitResult.GetActor()) { continue; } bool bAddResult = true; for (const FTargetingDefaultResultData& ResultData : TargetingResults.TargetResults) { if (ResultData.HitResult.GetActor() == HitResult.GetActor()) { bAddResult = false; break; } } if (bAddResult) { FTargetingDefaultResultData* ResultData = new(TargetingResults.TargetResults) FTargetingDefaultResultData(); ResultData->HitResult = HitResult; } } } }

UTargetingFilterTask_Team

bool UTargetingFilterTask_Team::ShouldFilterTarget(const FTargetingRequestHandle& TargetingHandle, const FTargetingDefaultResultData& TargetData) const { if (AActor* TargetActor = TargetData.HitResult.GetActor()) { if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle)) { if (SourceContext->SourceActor) { if (ULyraTeamSubsystem* TeamSubsystem = UWorld::GetSubsystem<ULyraTeamSubsystem>(SourceContext->SourceActor->GetWorld())) { return TeamSubsystem->CompareTeams(TargetActor, SourceContext->SourceActor) != TeamComparison; } } } } return true; }

UTargetingFilterTask_Block

bool UTargetingFilterTask_Block::ShouldFilterTarget(const FTargetingRequestHandle& TargetingHandle, const FTargetingDefaultResultData& TargetData) const { if (AActor* TargetActor = TargetData.HitResult.GetActor()) { if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle)) { if (SourceContext->SourceActor) { FHitResult OutHitResult; TArray<AActor*> ActorsToIgnore = {SourceContext->SourceActor, TargetActor}; return UKismetSystemLibrary::LineTraceSingle(TargetActor, SourceContext->SourceActor->GetActorLocation(), TargetData.HitResult.ImpactPoint, UEngineTypes::ConvertToTraceType(TraceChannel), false, ActorsToIgnore, EDrawDebugTrace::None, OutHitResult, true); } } } return false; }
http://www.jsqmd.com/news/674112/

相关文章:

  • 个人健身数据管理系统 Fitness-Tracker_HTML_v3.0
  • 国内半导体展哪家好?本土优质半导体展,高价值参展平台 - 品牌2026
  • 华为Pura 90系列发布 | 小艺解锁全新交互方式 更能干更懂你!
  • ArcMap转换坐标系
  • Dify对接API、数据库、AI模型全流程详解:3小时搭建可交付智能应用(附完整YAML模板)
  • 博客二:递归实战避坑指南,从入门到熟练运用
  • 跨境远程办公新体验!拖拽传文件让跨国协作丝滑不卡顿
  • ACPL-072L-500,3.3V/5V双电压高速CMOS光耦
  • ORA-39504 CRS通知失败,启动/关闭事件忽略怎么办?Oracle故障怎么修复和远程处理?
  • STC8A8K64D4开发板开箱体验:从零搭建你的第一个物联网小项目(附完整代码)
  • 未知物体自动标注流水线
  • 别再死记硬背UNet结构了!用PyTorch手把手拆解那个经典的U型编码-解码器
  • 暗黑破坏神2存档编辑器终极指南:5分钟打造你的完美游戏角色
  • 【微软MVP亲测】C# 14原生AOT×Dify客户端:如何用1个.csproj配置砍掉63% Azure Functions账单?
  • 如何将微信读书笔记转化为结构化知识资产:Obsidian Weread插件深度指南
  • 电动车续航计算:优化数据读取
  • Blazor组件生命周期陷阱大全,92%开发者踩过的6类内存泄漏+服务注入失效问题(含.NET 9 Preview 5验证报告)
  • 《应届生勇闯AI大厂都需要哪些技能?》(AI核心岗)
  • Kubernetes 如何部署微服务?
  • Dify多租户权限治理全攻略(从失控到可控的90天演进实录)
  • 终极Windows任务栏美化指南:RoundedTB让你的桌面焕然一新
  • Dify 2026边缘部署全链路拆解(含YAML模板+离线包校验SHA256值)
  • 爱毕业(aibiye)为数学建模论文提供高效复现与智能排版的一体化解决方案
  • 面向药品自动识别的YOLO26检测系统:Cipro/Ibuphil/Xyzall等4种药品及4种颜色联合检测(项目源码+数据集+模型权重+UI界面+python+深度学习+远程环境部署)
  • 靠谱的东莞高新技术企业认定培训公司
  • 基于YOLOv5的自动驾驶实时目标检测优化实战:从模型剪枝到TensorRT部署
  • JavaScript 中数组引用陷阱与“破纪录”问题的正确解法
  • 广州GEO优化多少钱?2026本地报价+真实行情,避开低价陷阱
  • 缓存基础概念与原理
  • 吊车地基承载力计算全攻略:从地勘报告到路基箱铺设,一文讲透