UE4:AI‘s MoveTo——代码分析
文章目录
- 移动流程概述
- 调整方向
- 关于路径
- UPathFollowingComponent
- 收到移动请求
- 更新路径
- 执行移动
移动流程概述
拉起一个UAITask_MoveTo,设置好InMoveRequest后,调用UAITask_MoveTo::PerformMove
,内部转到AAIController::MoveTo
。
判断是否到达,如果没有到达,就转到AAIController::RequestMove
开始请求移动,内部创建一条路径后,传给UPathFollowingComponent::RequestMove
,由UPathFollowingComponent
完成一条路径的移动。
RequestMove
调用SetMoveSegment
处理出移动线段的信息。最后在Tick内通过这些线段完成移动。
void UGameplayTask::PerformActivation()
void UAITask_MoveTo::Activate()
void UAITask_MoveTo::ConditionalPerformMove()
void UAITask_MoveTo::PerformMove()
FPathFollowingRequestResult AAIController::MoveTo(const FAIMoveRequest& MoveRequest, FNavPathSharedPtr* OutPath)
FAIRequestID AAIController::RequestMove(const FAIMoveRequest& MoveRequest, FNavPathSharedPtr Path)
FAIRequestID UPathFollowingComponent::RequestMove(const FAIMoveRequest& RequestData, FNavPathSharedPtr InPath)Path = InPath;
void UPathFollowingComponent::SetMoveSegment(int32 SegmentStartIndex)void UPathFollowingComponent::UpdateMoveFocus()FVector UPathFollowingComponent::GetMoveFocus(bool bAllowStrafe) constvoid AAIController::SetFocalPoint(FVector NewFocus, EAIFocusPriority::Type InPriority)
在Tick内,调用FollowPathSegment,借助移动组件来完成Pawn的移动
void UPathFollowingComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
void UPathFollowingComponent::FollowPathSegment(float DeltaTime)MovementComp->RequestPathMove(CurrentMoveInput);MovementComp->RequestDirectMove(MoveVelocity, bNotFollowingLastSegment);
如果使用了UCrowdFollowingComponent,则调用链则是:
void UCrowdManager::Tick(float DeltaTime)
void UCrowdManager::ApplyVelocity(UCrowdFollowingComponent* AgentComponent, int32 AgentIndex) const
void UCrowdFollowingComponent::ApplyCrowdAgentVelocity(const FVector& NewVelocity, const FVector& DestPathCorner, bool bTraversingLink, bool bIsNearEndOfPath)MovementComp->RequestPathMove(CurrentMoveInput);MovementComp->RequestDirectMove(MoveVelocity, bNotFollowingLastSegment);
调整方向
在过程中,会持续调用UpdateMoveFocus
,进行任务朝向的设置
void UPathFollowingComponent::UpdateMoveFocus()
void AAIController::SetFocalPoint(FVector NewFocus, EAIFocusPriority::Type InPriority)
AAIController在Tick时进行旋转
void AAIController::Tick(float DeltaTime)
void AAIController::UpdateControlRotation(float DeltaTime, bool bUpdatePawn)
关于路径
初始路径在AAIController::MoveTo
内确定,调用了找路径函数:
FPathFollowingRequestResult AAIController::MoveTo(const FAIMoveRequest& MoveRequest, FNavPathSharedPtr* OutPath)void AAIController::FindPathForMoveRequest(const FAIMoveRequest& MoveRequest, FPathFindingQuery& Query, FNavPathSharedPtr& OutPath) constUNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());if (NavSys){FPathFindingResult PathResult = NavSys->FindPathSync(Query);
找到路径后调用AAIController::RequestMove
,转到UPathFollowingComponent::RequestMove
const FAIRequestID RequestID = Path.IsValid() ? RequestMove(MoveRequest, Path) : FAIRequestID::InvalidRequest;PathFollowingComponent->RequestMove(MoveRequest, Path);
UPathFollowingComponent使用这个路径,完成实际移动逻辑的控制
UPathFollowingComponent
收到移动请求
FAIRequestID UPathFollowingComponent::RequestMove(const FAIMoveRequest& RequestData, FNavPathSharedPtr InPath) // 那条线段可能开始减速UpdateDecelerationData();// 细节优化,前面两个点选哪个作为起点const uint32 CurrentSegment = DetermineStartingPathPoint(InPath.Get());// 设置线段信息为起点SetMoveSegment(CurrentSegment);
设置线段信息
void UPathFollowingComponent::SetMoveSegment(int32 SegmentStartIndex)// 获取起点和终点const FVector SegmentStart = *PathInstance->GetPathPointLocation(MoveSegmentStartIndex);FVector SegmentEnd = *CurrentDestination;// 移动方向MoveSegmentDirection = (SegmentEnd - SegmentStart).GetSafeNormal();// 判断是否使用了NavLinkif (PathPt0.CustomLinkId){UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());INavLinkCustomInterface* CustomNavLink = NavSys->GetCustomLink(PathPt0.CustomLinkId);StartUsingCustomLink(CustomNavLink, SegmentEnd);}// 跟新移动焦点UpdateMoveFocus();
跟新移动焦点
void UPathFollowingComponent::UpdateMoveFocus()// 计算焦点const FVector MoveFocus = GetMoveFocus(AIOwner->bAllowStrafe);if (bAllowStrafe && DestinationActor.IsValid()){MoveFocus = DestinationActor->GetActorLocation();}else{const FVector CurrentMoveDirection = GetCurrentDirection();// 会往前挪5米MoveFocus = *CurrentDestination + (CurrentMoveDirection * FAIConfig::Navigation::FocalPointDistance);}// 设置到AIController上AIOwner->SetFocalPoint(MoveFocus, EAIFocusPriority::Move);
更新路径
void UPathFollowingComponent::UpdatePathSegment()//路径失效的处理if (!Path->IsValid())// 到达:碰撞if (bCollidedWithGoal)// 到达:到达检查可接受半径的线段else if (MoveSegmentEndIndex > PreciseAcceptanceRadiusCheckStartNodeIndex && HasReachedDestination(CurrentLocation))// 最后一段,且是MoveToGoal,直接走过去else if (bFollowingLastSegment && bMoveToGoalOnLastSegment && bLastPathChunk)CurrentDestination.Set(NULL, GoalLocation);UpdateMoveFocus();// 到达else if (HasReachedCurrentTarget(CurrentLocation))// 阻塞检测const bool bHasNewSample = UpdateBlockDetection();if (bHasNewSample && IsBlocked())OnPathFinished(EPathFollowingResult::Blocked, FPathFollowingResultFlags::None);
执行移动
void UPathFollowingComponent::FollowPathSegment(float DeltaTime)// 判断是否需要减速const bool bAccelerationBased = MovementComp->UseAccelerationForPathFollowing();if (bAccelerationBased)if (MoveSegmentStartIndex >= DecelerationSegmentIndex) if (bShouldDecelerate)const float SpeedPct = FMath::Clamp(FMath::Sqrt(DistToEndSq) / CachedBrakingDistance, 0.0f, 1.0f);CurrentMoveInput *= SpeedPct;MovementComp->RequestPathMove(CurrentMoveInput);// 直接移动FVector MoveVelocity = (CurrentTarget - CurrentLocation) / DeltaTime;PostProcessMove.ExecuteIfBound(this, MoveVelocity);MovementComp->RequestDirectMove(MoveVelocity, bNotFollowingLastSegment);
UE4:AI‘s MoveTo——代码分析相关推荐
- 海思AI芯片(Hi3519A/3559A)方案学习(十七)开发板上运行yolo3模型的代码分析
前言 前面的博客系列 已经介绍了如何将caffemodel转换成wk文件,如何将jpg文件转成bgr格式数据以及如何在PC上仿真模型推理等,基于这些基础,本文来结合代码分析如何在板子上推理yolov3 ...
- AI 生成的代码可信吗?编写的代码有 Bug 吗?
编译 | 禾木木 出品 | AI科技大本营(ID:rgznai100) 即使是帮助开发人员编写软件的工具也会产生类似的bug. 目前,大部分的软件开发人员会让 AI 帮助开发者们编写代码,但是开发人员 ...
- 控制元素显示和隐藏的方式及区别、内容溢出处理(区域滚动)、透明性、字体图标、常见字体、自定义字体设计、网站图标、CSS代码分析
元素的显示与隐藏: 常见控制元素的显示和隐藏的属性有display.visibility.overflow,需要清楚的理解三者之间的区别. display显示: 当值为none时为销毁对象,不保留原位 ...
- 管理Discuz!代码分析的收集整理
管理Discuz!代码分析的收集整理 1.后台结构 @�L^SD+k#G0 后台首页提供了常用操作:用户(组)编辑,论坛基本备份等. k XS7]+mB8H0 2.PHPChina 开源社区门户&qu ...
- Java调用百度AI实现人体属性分析
Java调用百度AI实现人体属性分析 好久没有更新了...闲来无事发一下模仿百度AI的人体属性分析. 百度AI效果图如下: 本人开发效果图如下: 界面大家可以忽略........下面讲讲代码实现 1. ...
- 未来属于无代码分析:每个人都能成为数据科学家
全文共1769字,预计学习时长6分钟 图源:unsplash 互联网诞生早期,创建网站是一门高端技术活.而现在,Wordpress这样的无代码工具让每个人都能迅速地创建一个网站.如今已有超50亿的网站 ...
- RPA与AI、低代码、BPM既同场竞技,又融合共生
<中智观察>第1714篇推送 作者:赵满满 编辑:益韩 编审:杨小天 头图来源:RPA中国/达观数据 如今,越来越多企业正在投资CRM,以便顺利运营并"取悦"自己的客户 ...
- 无代码六月大事件|2022无代码探索者大会即将召开;AI增强型无代码工具推出...
栏目导读:无代码资讯栏目从全球视角出发,带您了解无代码相关最新资讯. TOP 3 大事件 1.Neptune Software 在其 SAP 友好型开发平台中添加 AI 增强型无代码工具 6月23日, ...
- AI低代码平台遍地开花,AI对于低代码到底是帮手还是对手?
AI对于低代码平台到底是帮手还是对手? 近日,CSDN举办的新程序员大会中,对于AI在代码领域的能力进行了5个层级的定义.并且进行了大量的测试,发现当下的AI已经可以去到初级程序员了,而更为强大的GP ...
最新文章
- Vue 增加动态路由功能 【在原有系统上增加】
- windows10 查看 nvidia driver cuda 版本
- 八天学会MongoDB:第三天 细说高级操作
- 【收藏】OGC标准服务 WMS WCS WFS WPS
- go语言json解析的坑 注意事项
- 发现在创建云服务器ecs实例的磁盘快照时_玩转ECS第7讲|ECS数据保护-数据备份新特性与最佳实践...
- Android 系统(138 )---Mtk平台 Android 打包解包*.img ,修改system.img 参数
- java使用内部类的好处及其初始化
- Tensor:索引操作
- div contenteditable 富文本字数限制_知网查重是如何统计字数的?
- Spring中定义Bean的6种方式(声明式+编程式)
- win7计算机里没有网络图标,Win7网络图标不见了?找回网络图标的方法
- 移动安全工具:fastboot
- 获取非行内样式的兼容
- 上海交通大学学生生存手册
- 羚数智能入选 IDC关于中国制造执行系统(MES)的市场2021年度份额报告
- JavaWeb_07_Ajax
- Spring源码阅读笔记(一):整体架构与核心技术
- Codasip通过收购Cerberus增强RISC-V处理器设计的安全性
- 高NA镜头系统中的高级PSF计算
热门文章
- 51单片机PCB实物DCDC可调负电源负压+12V输入-2.5V~-9V输出500MA电流
- 【常见电路】稳压电路以及元器件的选型
- Django 上传文件出现 OSError Permission denied的解决方法
- 车载通信——LIN总线
- 哪个 DB2 9.7 发行版适合您?
- springboot消费kafka设置topics 以及 groupId
- 【算法学习】最小生成树
- Java调用Python:实现两大语言的无缝对接
- [转]Java中文处理学习笔记——Hello Unicode
- HTMlEncode和HTMLDecode、UrlEncode和 UrlDecode 定义和用法