UE4 中射线检测的简单探索
通过源码了解实现简单的实现原理。非常粗浅,只涉及一些基本的调用路径。
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]);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律