목차
1. Detour Crowd란
2. AddAgent
3. SetMoveTarget
4. CrowdUpdate
1. Detour Crowd란
RecastDemo 프로젝트에서는 프로그램을 실행하고 네비게이션을 빌드한 후, 경로 위에 dtAgent 객체들을 배치하고 임의의 지점으로 이동명령을 내릴 수 있다.dtAgent는 길 위에 배치하고 움직이게 만들 수 있는 하나의 길찾기 주체이며, DetourCrowd는 dtAgent 객체들의 군집을 일컫는 말이다. 스타크래프트에 익숙한 우리네 정서에 맞게 dtAgent는 지금부터 유닛이라 부르겠다.
2. AddAgent
void CrowdToolState::addAgent(const float* p)
{
if (!m_sample) return;
dtCrowd* crowd = m_sample->getCrowd();
dtCrowdAgentParams ap;
memset(&ap, 0, sizeof(ap));
ap.radius = m_sample->getAgentRadius();
ap.height = m_sample->getAgentHeight();
ap.maxAcceleration = 8.0f;
ap.maxSpeed = 3.5f;
ap.collisionQueryRange = ap.radius * 12.0f;
ap.pathOptimizationRange = ap.radius * 30.0f;
ap.updateFlags = 0;
if (m_toolParams.m_anticipateTurns)
ap.updateFlags |= DT_CROWD_ANTICIPATE_TURNS;
if (m_toolParams.m_optimizeVis)
ap.updateFlags |= DT_CROWD_OPTIMIZE_VIS;
if (m_toolParams.m_optimizeTopo)
ap.updateFlags |= DT_CROWD_OPTIMIZE_TOPO;
if (m_toolParams.m_obstacleAvoidance)
ap.updateFlags |= DT_CROWD_OBSTACLE_AVOIDANCE;
if (m_toolParams.m_separation)
ap.updateFlags |= DT_CROWD_SEPARATION;
ap.obstacleAvoidanceType = (unsigned char)m_toolParams.m_obstacleAvoidanceType;
ap.separationWeight = m_toolParams.m_separationWeight;
int idx = crowd->addAgent(p, &ap);
if (idx != -1)
{
if (m_targetRef)
crowd->requestMoveTarget(idx, m_targetRef, m_targetPos);
// Init trail
AgentTrail* trail = &m_trails[idx];
for (int i = 0; i < AGENT_MAX_TRAIL; ++i)
dtVcopy(&trail->trail[i*3], p);
trail->htrail = 0;
}
}
dtCrowdAgentParams는 CrowdAgent, 즉 유닛의 길찾기 관련 설정을 지정하기 위해 쓰이는 구조체이다. 유닛의 크기, 높이, 최대 속도와 최대 가속도, 길찾기 원칙 등을 저장한다. 이 매개변수 구조체의 필드값을 알맞게 설정하고 dtCrowd->addAgent 함수를 호출하면 유닛이 생성된다.
3. SetMoveTarget
void CrowdToolState::setMoveTarget(const float* p, bool adjust)
{
......
navquery->findNearestPoly(p, halfExtents, filter, &m_targetRef, m_targetPos);
......
{
for (int i = 0; i < crowd->getAgentCount(); ++i)
{
const dtCrowdAgent* ag = crowd->getAgent(i);
if (!ag->active) continue;
crowd->requestMoveTarget(i, m_targetRef, m_targetPos);
}
}
}
}
유닛을 생성하고 이동모드로 지면을 클릭하면 클릭된 위치로 유닛이 이동한다. 이 함수의 코드의 양도 여차저차 내용이 길지만, 핵심적인 부분은 클릭한 지점으로부터 가장 가까운 지점을 query->findnearestPoly 함수로 찾아 목표지로 지정하는 부분과 유닛 군집(dtCrowdAgent)을 순회하며 개개의 유닛들에게 requestMoveTarget 함수를 통해 목표지까지의 이동을 부탁하는 부분이다. 필요할때맏 유닛들의 길찾기 상태만 이렇게 바꾸어 주면 유닛들이 매 순간 이동하면서 생기는 필요연산은 CrowdUpdate 함수에서 처리된다.
4. CrowdUpdate
void CrowdToolState::updateTick(const float dt)
{
if (!m_sample) return;
dtNavMesh* nav = m_sample->getNavMesh();
dtCrowd* crowd = m_sample->getCrowd();
if (!nav || !crowd) return;
TimeVal startTime = getPerfTime();
if (m_toolParams.m_showCorners)
crowd->update(dt, &m_agentDebug);
TimeVal endTime = getPerfTime();
// Update agent trails
for (int i = 0; i < crowd->getAgentCount(); ++i)
{
const dtCrowdAgent* ag = crowd->getAgent(i);
AgentTrail* trail = &m_trails[i];
if (!ag->active)
continue;
// Update agent movement trail.
trail->htrail = (trail->htrail + 1) % AGENT_MAX_TRAIL;
dtVcopy(&trail->trail[trail->htrail * 3], ag->npos);
}
m_agentDebug.vod->normalizeSamples();
m_crowdSampleCount.addSample((float)crowd->getVelocitySampleCount());
m_crowdTotalTime.addSample(getPerfTimeUsec(endTime - startTime) / 1000.0f);
}
군집들의 상태설정만 필요할때마다 해주면 각각의 길찾기 주체들의 상태 업데이트는 crowd->Update 함수만 주기적으로 호출하면 알아서 실행된다. RecastDemo 프로젝트에서는 updateTick이라는 함수에서 군집의 Update 함수를 호출해준다.
다음은 지금까지 파악한 네비게이션 메시 빌드와 dtAgent,dtCrowd들을 이용해 나의 게임엔진에 길찾기 시스템을 이식하는 내용을 다룰 것이다.
'자체엔진 Yunuty > RecastNavigation' 카테고리의 다른 글
자체 게임 엔진에 RecastNavigation 이식하기 (0) | 2023.11.01 |
---|---|
RecastNavigation의 경로계산 (0) | 2023.10.23 |
RecastNavigation 경로맵 빌드 코드의 분석 (0) | 2023.10.19 |