一、導航數據的構造
導航數據的主體來自幾何圖形,但是離線聯通就不是來自幾何圖形,所以要額外處理。從軟件實現的角度看,這個額外處理要具有通用性。UE通用是通過INavRelevantInterface和FNavigationRelevantData來實現,兩個結構的名字也非常直觀,一個是“導航相關接口”,一個是“導航相關數據”,並且INavRelevantInterface的GetNavigationData接口負責填充FNavigationRelevantData數據。
Engine\Source\Runtime\NavigationSystem\Private\NavigationOctree.cpp
void FNavigationOctree::AppendToNode(const FOctreeElementId2& Id, INavRelevantInterface* NavElement, const FBox& Bounds, FNavigationOctreeElement& Element)
{
……
if (NavElement)
{
SCOPE_CYCLE_COUNTER(STAT_Navigation_GatheringNavigationModifiersSync);
const bool bDoInstantGathering = !IsLazyGathering(*NavElement);
if (bDoInstantGathering)
{
NavElement->GetNavigationData(*Element.Data);
}
else
{
Element.Data->bPendingChildLazyModifiersGathering = true;
}
}
……
}
二、ANavLinkProxy
可以看到,ANavLinkProxy類派生自INavRelevantInterface,所以它也提供了生成導航數據相關的接口。
class AIMODULE_API ANavLinkProxy : public AActor, public INavLinkHostInterface, public INavRelevantInterface
其中比較關系的接口就是GetNavigationData接口,這個接口的實現比較簡單
void ANavLinkProxy::GetNavigationData(FNavigationRelevantData& Data) const
{
NavigationHelper::ProcessNavLinkAndAppend(&Data.Modifiers, this, PointLinks);
NavigationHelper::ProcessNavLinkSegmentAndAppend(&Data.Modifiers, this, SegmentLinks);
}
可以看到,它只是簡單的把PointLinks(也就是SimpleLinks)節點添加到導航數據中,這些對應Details面板中的聯通點信息。這里要注意的是,Smart Link對應的數據在這里沒有做任何處理。
三、SmartLink的處理
在Actor的Details面板上看到的SmartLink組件對應的類成員為
UNavLinkCustomComponent* SmartLinkComp;
從派生關系上看,UNavLinkCustomComponent派生自UNavRelevantComponent,也就是一個組件對象。
class NAVIGATIONSYSTEM_API UNavLinkCustomComponent : public UNavRelevantComponent, public INavLinkCustomInterface
組件對象有一個優點,就是可以運行時注冊/卸載。這也是文檔中提到的smart links的特點
Smart Links
Disabled by default
Turn on by ticking Smart Link Is Relevant
Can be turned on and off at runtime
And will notify nearby actors who want to know!
在組件注冊的回調函數中
UNavLinkCustomComponent::OnRegister===>>>UNavigationSystemV1::RequestCustomLinkRegistering===>>>UNavigationSystemV1::RegisterCustomLink===>>>
CustomLinksMap.Add(LinkId, FNavigationSystem::FCustomLinkOwnerInfo(&CustomLink));
CustomLinksMap包含的INavLinkCustomInterface接口中有
/** Get basic link data: two points (relative to owner) and direction */
virtual void GetLinkData(FVector& LeftPt, FVector& RightPt, ENavLinkDirection::Type& Direction) const {};
可以獲得Link的兩個點和方向,這個也是smartlink中最為關鍵的兩個維度。
UNavLinkCustomComponent類也繼承了INavLinkCustomInterface接口,當通過INavLinkCustomInterface接口獲得聯通數據時,也是通過GetLinkData獲得的,所以這個是獲得Link的唯一入口。UNavLinkCustomComponent::GetNavigationData===>>>UNavLinkCustomComponent::GetLinkModifier===>>>INavLinkCustomInterface::GetModifier
FNavigationLink INavLinkCustomInterface::GetModifier(const INavLinkCustomInterface* CustomNavLink)
{
FNavigationLink LinkMod;
LinkMod.SetAreaClass(CustomNavLink->GetLinkAreaClass());
LinkMod.UserId = CustomNavLink->GetLinkId();
ENavLinkDirection::Type LinkDirection = ENavLinkDirection::BothWays;
CustomNavLink->GetLinkData(LinkMod.Left, LinkMod.Right, LinkDirection);
CustomNavLink->GetSupportedAgents(LinkMod.SupportedAgents);
LinkMod.Direction = LinkDirection;
return LinkMod;
}
四、通知到達特定位置
在smart link注冊的時候,會為它生成唯一的userid
void UNavLinkCustomComponent::OnRegister()
{
Super::OnRegister();
if (NavLinkUserId == 0)
{
NavLinkUserId = INavLinkCustomInterface::GetUniqueId();
UE_LOG(LogNavLink, VeryVerbose, TEXT("%s new navlink id %u [%s]."), ANSI_TO_TCHAR(__FUNCTION__), NavLinkUserId, *GetFullName());
}
UNavigationSystemV1::RequestCustomLinkRegistering(*this, this);
}
在尋路的時候如果用到了這些節點,並且判斷這些離線link的userid有效,則可以發送通知。
void UPathFollowingComponent::SetMoveSegment(int32 SegmentStartIndex)
{
……
// handle moving through custom nav links
if (PathPt0.CustomLinkId)
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());
INavLinkCustomInterface* CustomNavLink = NavSys->GetCustomLink(PathPt0.CustomLinkId);
StartUsingCustomLink(CustomNavLink, SegmentEnd);
}
……
}
五、offmesh link路徑點的標志
bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, int* outDataSize)
{
……
if (offMeshCon.type & DT_OFFMESH_CON_POINT)
{
dtPoly* p = &navPolys[offMeshPolyBase+n];
p->vertCount = 2;
p->verts[0] = (unsigned short)(offMeshVertsBase + n*2+0);
p->verts[1] = (unsigned short)(offMeshVertsBase + n*2+1);
p->flags = offMeshCon.polyFlag;
p->setArea(offMeshCon.area);
p->setType(DT_POLYTYPE_OFFMESH_POINT);
n++;
}
……
}
六、smart和simple的關系
兩者本質上是沒什么必然聯系的,它們可以同時存在,也可以同時被尋路系統使用(這個結論沒有真正驗證)。
