CrowdToolState::updateTick(constfloatdt)
作为寻路模拟过程的主要函数主要就是干3件事:
1 crowd->update(dt, &m_agentDebug);
2 Update agent trails表示agent的足迹,不是寻路的必要部分,可以不用
3 m_agentDebug.vod->normalizeSamples();
后面两步都是用来demo展示用的
主要逻辑:
dtCrowd::update(constfloatdt,dtCrowdAgentDebugInfo*debug):
当m_state->hilightAgent(ahit);选择了一个agent的时候,便设置了m_agentDebug.idx
debugIdx中就记录了我们点亮的是哪个agent
agent的active在remove的时候设置为false
接下来就是寻路部分:
关于寻路过程我的理解:
首先当接到request的时候,先进行短寻路
寻路不到target就进行长寻路
长寻路有迭代次数的限制不至于让一个tick走的太多
必须长寻路因为全靠短寻路结果可能根本不是最好的,可否一定能寻到目的地?
然后每隔一段时间进行optimize
为了处理出现误差和由于动态阻挡而导致的当前路径已经不是最优的情况
几个例子:之前被动态阻挡导致需要转弯,但是动态阻挡已经不在那了,这个时候隔一段时间进行一次优化是有意义的
optimize结果可能有两种:一是跟之前的路径能连上,另一种是连不上,连不上的话直接返回,也就是说没有进行优化。
那么如果当前的路径遇到了阻挡呢?就是在checkPathValidity中检测出来申请重新寻路。
步骤:
1 checkPathValidity
总得来说就是先确保startpos和startRef的有效性
以及在ag->corridor.fixPathStart确保path当中的第一个polygon是有效的
其实就是让path[0]为agentRef
而留下其他的不管,让replanner去调整
其他还有三种需要replan的情况
2 updateMoveRequest
InitSlicedFindPath 做寻路的初始化工作
updateSlicedFindPath 主要工作:
相当于进行了一次类似A*的短寻路,并且记录结果
这个过程首先当接收到request的时候进行短寻路,最后收集寻路结果的时候,如果是刚才的replan的情况,也就是当前路径是正在被重新规划,则执行
finalizeSlicedFindPathPartial尽可能使用已有路径,否则说明目标被重置,需要执行finalizeSlicedFindPath
finalizeSlicedFindPath翻转了updateSlicedFindPath的类似A*寻路的路径结果并保存。
经过短寻路没走到头的ag->targetState= DT_CROWDAGENT_TARGET_WAITING_FOR_QUEUE
并且将agent addToPathQueue,然后再dtPathQueue::update中
然后进行一次长寻路
这次长寻路是从ag->corridor.getLastPoly()到ag->targetRef
然后将两次寻路的结果merge到一块
放到res中
然后ag->corridor.setCorridor(targetPos, res,nres);
3 updateTopologyOptimization
https://blog.csdn.net/icebergliu1234/article/details/88393735
4 对每一个agent的边界 进行更新,并且找到neibour放到neis里面
在m_grid分配一块item记录agent
for (inti= 0; i < nagents;++i)
{
ap.collisionQueryRange = ap.radius * 12.0f;
表示多长的距离的移动算是移动了
updateThr =ag->params.collisionQueryRange*0.25f;是否进行update更新碰撞边界的阈值
ag->npos, ag->boundary.getCenter()这两个的距离超过了updateThr
或者ag->boundary中的poly有无效的,说明需要update
接下来更新边界
dtLocalBoundary::update
在移动了一定的距离后,更新碰撞边界
或者碰撞边界已经变得invalid
并且ag->nneis= getNeighbours查询neighbour agents并且记录在ag->neis里面
}
5 接下来找到要前往的corners
for (inti= 0; i < nagents;++i)
{
ag->corridor.findCorners
corners的信息被存储在dtCrowdAgent里的三个数组里面
float cornerVerts[DT_CROWDAGENT_MAX_CORNERS*3];
unsigned char cornerFlags[DT_CROWDAGENT_MAX_CORNERS];目前是三种,起始,终点,offmeshconnection
dtPolyRef cornerPolys[DT_CROWDAGENT_MAX_CORNERS];里面是poly的reference id
如果agent启用了DT_CROWD_OPTIMIZE_VIS也就是使用optimizePathVisibility来优化path
做的事情就是shortcut到下一个可见的corner
}找到前往的corners完毕
6 接下来触发所有的off-meshconnections 这一段主要用来表现,所以可以由项目游戏模块自己来实现
for (inti= 0; i < nagents;++i)
{
triggerRadius是触发半径
用overOffmeshConnection(constdtCrowdAgent*ag,constfloatradius)判断是否经过了OffmeshConnection
如果是corners里面有OffmeshConnection,肯定是最后一个
因为在findcorners里面就是这么设定的
所以判断ag->cornerFlags[ag->ncorners-1]& DT_STRAIGHTPATH_OFFMESH_CONNECTION
如果当前位置ag->npos与这个cornner的距离小于触发半径则overOffmeshConnection返回true
if(overOffmeshConnection(ag,triggerRadius))
{
说明triggerRadius范围内有OffmeshConnection
ag->corridor.moveOverOffmeshConnection(ag->cornerPolys[ag->ncorners-1],refs,anim->startPos,anim->endPos,m_navquery))
这个函数找到了OffmeshConnection的两个端点,作为anim->startPos和anim->endPos
如果找到了
设置这个agent的dtCrowdAgentAnimation* anim
设置播放时间tmax为以两倍ag->params.maxSpeed速度走完跳点的时间
并且ag->