虚幻学习笔记21—C++中实现射线检测

发布时间:2023年12月25日

一、前言

? ? ? ? 虚幻中射线检测根据通道分为单通道和多通道检测,根据检测目标分为通道检测和目标物体类型检测。虚幻的射线检测都是直线形状,而unity(又拿来对比了)不仅有直线形状射线检测,还有球形等其他多边形的检测,对比起来虚幻就有点匮乏了。

二、实现

? ? ? ? 使用任何射线检测都要定义起始位置坐标和方向,同时还要设置变量的值,一般都和摄像机的进行绑定,结束位置尽量设置的足够远。

	//单射线检测
	FVector startLocation;//开始位置
	FVector forwardDir;//前向方向向量
	FVector	endLocation;

	//使用射线检测
	startLocation = MyCam->GetComponentLocation();
	forwardDir = MyCam->GetForwardVector();
	endLocation = startLocation + forwardDir * 9999;
2.1、单通道射线检测

? ? 除了定义射线的其实位置和方向,还要定义射线碰撞返回的数据,这个是最关键和重要的数据。

	FHitResult hitResult;//射线碰撞返回的数据

1、根据通道检测LineTraceSingleByChannel

????????其中“ECC_Visibility”即为通道类型,对应蓝图中的碰撞预设,如图2.1.1所示

	bool bTempHit = GetWorld()->LineTraceSingleByChannel(hitResult, startLocation, endLocation, ECC_Visibility);//最后一个为通道类型,根据通道进行检测
	if (bTempHit)
	{
		AActor* tempHitActor = hitResult.GetActor();
		FVector tempImpactPoint = hitResult.ImpactPoint;//射线击中的点
		FVector tempHitLocation = hitResult.Location;//击中点的位置
		GEngine->AddOnScreenDebugMessage(-1, 5.0, FColor::Yellow, FString::Printf(TEXT("hit name:%s"), *tempHitActor->GetName()));
	}
图2.1.1

注意:如过设置为“ECC_Visibility”,那么被检测的对象的“Visibility”这里就要设置为阻挡状态,否则射线也将检测不到。

2、根据目标物体类型进行检测LineTraceSingleByObjectType

? ? ? 目标物体类型通过定义FCollisionObjectQueryParams的参数然后用AddObjectTypesToQuery方法添加目标通道进去,看起来和通过通道很类似,

	FCollisionObjectQueryParams tempObjectType;
	tempObjectType.AddObjectTypesToQuery(ECC_WorldDynamic);
	//	tempObjectType.AddObjectTypesToQuery(ECC_WorldStatic);
	bool bTempHit2 = GetWorld()->LineTraceSingleByObjectType(hitResult, startLocation, endLocation, tempObjectType);
	if (bTempHit2)
	{
		AActor* tempHitActor = hitResult.GetActor();
		FVector tempImpactPoint = hitResult.ImpactPoint;//射线击中的点
		FVector tempHitLocation = hitResult.Location;//击中点的位置
		GEngine->AddOnScreenDebugMessage(-1, 5.0, FColor::Green, FString::Printf(TEXT("hit name:%s"), *tempHitActor->GetName()));
	}
3、根据Profile的名称进行检测LineTraceSingleByProfile

1)还可以根据Profile的类型进行检测,Profile为如图2.1.2所示的碰撞预设的名字,这个名字是可

图2.1.2

以在项目设置-》碰撞-》Preset中可以新建,如图2.1.3所示新建一个“CPPTest"类型的碰撞预设

图2.1.3
注:设置完后最好要编译一下

? ? ? ? 然后在代码中设置参数的名字即为这个”CPPTest",代码如下,

	bool bTempHit5 = GetWorld()->LineTraceSingleByProfile(hitResult, startLocation, endLocation, "CPPTest");
	if (bTempHit5)
	{
		AActor* tempHitActor = hitResult.GetActor();
		GEngine->AddOnScreenDebugMessage(-1, 5.0, FColor::Green, FString::Printf(TEXT("hit name:%s"), *tempHitActor->GetName()));
	}

注意:这里添加的“CPPTest"后是根据图2.1.3中的设置与其他类型的阻挡、忽略有关,比如这里设置Pawn为阻挡,碰撞目标物体的碰撞对象类型为Pawn,那么无论其碰撞响应设置什么都可以发生射线检测,如图2.1.4所示为射线检测目标物体的碰撞设置,其对象类型为Pawn。

图2.1.4
2)该方法还可以添加忽略检测哪个Actor,代码如下,通过FCollisionQueryParams类型参数可以忽略掉不需要检测的对象。
	FCollisionQueryParams tempQueryParams;
	tempQueryParams.AddIgnoredActor(this->GetUniqueID());

	//CPPTest为新建的碰撞预设
	bool bTempHit5 = GetWorld()->LineTraceSingleByProfile(hitResult, startLocation, endLocation, "CPPTest", tempQueryParams);
	if (bTempHit5)
	{
		AActor* tempHitActor = hitResult.GetActor();
		GEngine->AddOnScreenDebugMessage(-1, 5.0, FColor::Green, FString::Printf(TEXT("hit name:%s"), *tempHitActor->GetName()));
	}
4、
2.2、多通道射线检测

多通道可以通过FCollisionObjectQueryParams的AddObjectTypesToQuery添加,多通道就意味着多个结果,因此也需要有一个数组来接收结果。

1、根据通道直接使用LineTraceMultiByChannel

? ? ? ? 和单通道的使用没什么区别,但是这里可以返回多个结果。

	多射线检测通道检测
	bool bTempHit3 = GetWorld()->LineTraceMultiByChannel(listHitResult, startLocation, endLocation, ECC_Visibility);
	if (bTempHit3)
	{
		for (int32 i = 0; i < listHitResult.Num(); i++)
		{
			AActor* tempHitActor = listHitResult[i].GetActor();
			GEngine->AddOnScreenDebugMessage(-1, 5.0, FColor::Green, FString::Printf(TEXT("hit name:%s"), *tempHitActor->GetName()));
		}
	}

2、多对象类型检测LineTraceMultiByObjectType

	
	FCollisionObjectQueryParams tempObjectType;
	tempObjectType.AddObjectTypesToQuery(ECC_WorldDynamic);

    tempObjectType.AddObjectTypesToQuery(ECC_Visibility);
	bool bTempHit4 = GetWorld()->LineTraceMultiByObjectType(listHitResult, startLocation, endLocation, tempObjectType);
	if (bTempHit4)
	{
		for (int i = 0; i < listHitResult.Num(); i++)
		{
			AActor* tempHitActor = listHitResult[i].GetActor();
			GEngine->AddOnScreenDebugMessage(-1, 5.0, FColor::Green, FString::Printf(TEXT("hit name:%s"), *tempHitActor->GetName()));
		}
	}

3、根据碰撞预设名LineTraceMultiByProfile

? ? ? ? 参考2.1的单个LineTraceSingleByProfile

2.3、其他形状的扫描射线SweepSingleByChannel

? ? ? ? 这个函数需要提前定义好扫描的形状,虚幻没有预设的形状,可以通过大小、类型、方向来决定最终的扫描形状效果,另外可以通过?? ?GetWorld()->bDebugDrawAllTraceTags = true;来

	FCollisionShape tempBoxShape = FCollisionShape::MakeBox(FVector(10, 10, 10));
	FQuat tempRot = FQuat::MakeFromEuler(FVector(0, 0, 0));
	bTempHit = GetWorld()->SweepSingleByChannel(hitResult, startLocation, endLocation, tempRot, ECC_WorldDynamic, tempBoxShape);
	GetWorld()->bDebugDrawAllTraceTags = true;

呈现最终的形状,如图2.3.1所示,从摄像机的位置发射了一个蓝色的Box形状的射线

图2.3.1

????????其他的函数SweepMultiByObjectType、SweepMultiByProfile的使用方式类似,可以参考上述的使用方式。

2.4、异步射线扫描AsyncLineTraceByChannel

? ? ? ? 这个函数和之前其他的稍微有点不一样,它不在会直接返回是否碰撞结果,需要借助一个回调函数来处理得到的结果,所以先定义回调函数和委托,然后将这个函数实现、委托绑定函数

//异步射线检测的回调函数
FTraceDelegate traceDelegate;
void AsynLlineTraceFunc(const FTraceHandle& handel, FTraceDatum& datum);


void AMyCharacter1::AsynLlineTraceFunc(const FTraceHandle& handel, FTraceDatum& datum)
{
	//GEngine->AddOnScreenDebugMessage(-1, 5.0, FColor::Green, FString::Printf(TEXT("AsynLlineTraceFunc:%d"), datum.OutHits.Num()));
	for (int32 i = 0; i < datum.OutHits.Num(); i++)
	{
		AActor* tempHitActor = datum.OutHits[i].GetActor();
		GEngine->AddOnScreenDebugMessage(-1, 5.0, FColor::Green, FString::Printf(TEXT("hit name:%s"), *tempHitActor->GetName()));
	}
}

接下来就是使用异步射线函数,在设置参数的时候即可以选择使用多通道还是单通道,注意:这里

//异步处理
traceDelegate.BindUObject(this, &AMyCharacter1::AsynLlineTraceFunc);
const FCollisionResponseParams tempResponsParam = FCollisionResponseParams::DefaultResponseParam;
GetWorld()->AsyncLineTraceByChannel(EAsyncTraceType::Multi, startLocation, endLocation, ECC_WorldDynamic, tempQueryParams, tempResponsParam, &traceDelegate);

FCollisionResponseParams可以使用默认的而碰撞查询函数不要使用默认的,否则会不发生任何碰撞,这个参数就是要忽略掉不要检测的物体类型。

	FCollisionQueryParams tempQueryParams;
	tempQueryParams.AddIgnoredActor(this->GetUniqueID());

其他的异步函数AsyncLineTraceByProfile、AsyncLineTraceByObjectType等使用方式可以参考上述的方法。

三、总结

3.1、LineTraceSingleByProfile的Profile是碰撞预设,可以在项目设置里自定义,射线的碰撞是否能发生和这里设置和其他对象类型是否为阻挡有关,当设置这个类型为阻挡就可以检测到该对象。

3.2、另外可以通过?? ?GetWorld()->bDebugDrawAllTraceTags = true;来显示所有的射线效果。

3.2、异步射线检测的方法不会直接返回结果,需要一个回调函数来处理结果,忽略参数最好不要使用默认的。

文章来源:https://blog.csdn.net/zhangxiao13627093203/article/details/135124125
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。