透過原始碼瞭解實現簡單的實現原理。非常粗淺,只涉及一些基本的呼叫路徑。
LineTrace...這個函式實際上是呼叫的TSceneCastCommon這個模板函式
template <typename Traits, typename TGeomInputs>
bool TSceneCastCommon(const UWorld* World, typename Traits::TOutHits& OutHits, const TGeomInputs& GeomInputs, const FVector Start, const FVector End, ECollisionChannel TraceChannel, const struct FCollisionQueryParams& Params, const struct FCollisionResponseParams& ResponseParams, const struct FCollisionObjectQueryParams& ObjectParams)
首先它是呼叫的RaycastSingle函式
using TCastTraits = TSQTraits<FHitRaycast, ESweepOrRay::Raycast, ESingleMultiOrTest::Single>;
return TSceneCastCommon<TCastTraits>(World, OutHit, FRaycastSQAdditionalInputs(), Start, End, TraceChannel, Params, ResponseParams, ObjectParams);
TSQTraits結構體
該結構體會儲存或者設定我們檢測中需要用到的變數,利用上方的using進行初始賦值
其中最主要的就是
using THitBuffer = typename TChooseClass<InSingleMultiOrTest == ESingleMultiOrTest::Multi, FDynamicHitBuffer<InHitType>, typename TChooseClass<InGeometryQuery == ESweepOrRay::Sweep, FSingleHitBuffer<FHitSweep>, FSingleHitBuffer<FHitRaycast>>::Result >::Result;
後續會出現的函式
IsRay() 檢測當前的掃描型別
constexpr static bool IsRay() { return GeometryQuery == ESweepOrRay::Raycast; }
GetNumHits() 檢測命中的數量
針對multi和single有不同的檢測返回
// GetNumHits - multi
template <ESingleMultiOrTest T = SingleMultiOrTest>
static typename TEnableIf<T == ESingleMultiOrTest::Multi, int32>::Type GetNumHits(const THitBuffer& HitBuffer)
{
return HitBuffer.GetNumHits();
}
// GetNumHits - single/test
template <ESingleMultiOrTest T = SingleMultiOrTest>
static typename TEnableIf<T != ESingleMultiOrTest::Multi, int32>::Type GetNumHits(const THitBuffer& HitBuffer)
{
return GetHasBlock(HitBuffer) ? 1 : 0;
}
HitBuffer.GetNumHits()
其呼叫的是根據TChooseClass選擇出來的掃描型別的命中結果中的函式
FORCEINLINE int32 GetNumHits() const
{
return Hits.Num();
}
而這些實際的HitBuffer類裡面存在著一個Hits成員變數
//Engine\Source\Runtime\PhysicsCore\Public\PhysXInterfaceWrapperCore.h
/** Hits encountered. Can be larger than HIT_BUFFER_SIZE */
TArray<TTypeCompatibleBytes<HitType>, TInlineAllocator<HIT_BUFFER_SIZE>> Hits;
//當發生碰撞時會執行該回撥函式,向Hits陣列裡面加入資料
virtual PxAgain processTouches(const HitType* buffer, PxU32 nbHits) override
{
Hits.Append((TTypeCompatibleBytes<HitType>*)buffer, nbHits);
return true;
}
TSceneCastCommon函式
在該函式中,首先會進行射線檢測距離的計算
FVector Delta = End - Start;
float DeltaSize = Delta.Size();
float DeltaMag = FMath::IsNearlyZero(DeltaSize) ? 0.f : DeltaSize;
float MinBlockingDistance = DeltaMag;
然後如果有新增了Ignore的物件,會進行處理。
接著就會開始進行檢測了,Traits就是TSQTraits結構體
typename Traits::THitBuffer HitBufferSync; //HitBufferSync用來儲存命中的快取結果
bool bBlockingHit = false;
const FVector Dir = DeltaMag > 0.f ? (Delta / DeltaMag) : FVector(1, 0, 0);
const FTransform StartTM = Traits::IsRay() ? FTransform(Start) : FTransform(*GeomInputs.GetGeometryOrientation(), Start); //透過判斷檢測型別
接著會對當前的物理場景進行凍結,避免其他因素的影響
// Enable scene locks, in case they are required
FPhysScene& PhysScene = *World->GetPhysicsScene();
FScopedSceneReadLock SceneLocks(PhysScene);
{
FScopedSQHitchRepeater<decltype(HitBufferSync)> HitchRepeater(HitBufferSync, QueryCallback, FHitchDetectionInfo(Start, End, TraceChannel, Params));
do
{
Traits::SceneTrace(PhysScene, GeomInputs, Dir, DeltaMag, StartTM, HitchRepeater.GetBuffer(), Traits::GetHitFlags(), Traits::GetQueryFlags(), Filter, Params, &QueryCallback);
} while (HitchRepeater.RepeatOnHitch());
}
凍結之後會進行命中檢測
const int32 NumHits = Traits::GetNumHits(HitBufferSync);
透過獲得的NumHits個數來判斷是否成功命中,然後返回最近的命中結果
if(NumHits > 0 && GetHasBlock(HitBufferSync))
{
bBlockingHit = true;
MinBlockingDistance = GetDistance(Traits::GetHits(HitBufferSync)[NumHits - 1]);
}