? ? ? ? 虚幻中射线检测根据通道分为单通道和多通道检测,根据检测目标分为通道检测和目标物体类型检测。虚幻的射线检测都是直线形状,而unity(又拿来对比了)不仅有直线形状射线检测,还有球形等其他多边形的检测,对比起来虚幻就有点匮乏了。
? ? ? ? 使用任何射线检测都要定义起始位置坐标和方向,同时还要设置变量的值,一般都和摄像机的进行绑定,结束位置尽量设置的足够远。
//单射线检测
FVector startLocation;//开始位置
FVector forwardDir;//前向方向向量
FVector endLocation;
//使用射线检测
startLocation = MyCam->GetComponentLocation();
forwardDir = MyCam->GetForwardVector();
endLocation = startLocation + forwardDir * 9999;
? ? 除了定义射线的其实位置和方向,还要定义射线碰撞返回的数据,这个是最关键和重要的数据。
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()));
}
注意:如过设置为“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()));
}
1)还可以根据Profile的类型进行检测,Profile为如图2.1.2所示的碰撞预设的名字,这个名字是可
以在项目设置-》碰撞-》Preset中可以新建,如图2.1.3所示新建一个“CPPTest"类型的碰撞预设
? ? ? ? 然后在代码中设置参数的名字即为这个”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。
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()));
}
多通道可以通过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
? ? ? ? 这个函数需要提前定义好扫描的形状,虚幻没有预设的形状,可以通过大小、类型、方向来决定最终的扫描形状效果,另外可以通过?? ?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形状的射线
????????其他的函数SweepMultiByObjectType、SweepMultiByProfile的使用方式类似,可以参考上述的使用方式。
? ? ? ? 这个函数和之前其他的稍微有点不一样,它不在会直接返回是否碰撞结果,需要借助一个回调函数来处理得到的结果,所以先定义回调函数和委托,然后将这个函数实现、委托绑定函数
//异步射线检测的回调函数
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、异步射线检测的方法不会直接返回结果,需要一个回调函数来处理结果,忽略参数最好不要使用默认的。