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]);
}

作者:XTG111

出处:https://www.cnblogs.com/XTG111/p/18391594

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   XTG111  阅读(96)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示