转自:https://blog.csdn.net/qq_29523119/article/details/85957072
在游戏中我们经常用到光线检测(RayTrace),也就是直线与物体相交,获取相交的对象和相交的位置等等。
UE4中 "KismetSystemLibrary.h"和 "Wolrd.h"提供了几个有关光线检测(RayTrace)的接口,这一节只要介绍和 “KismetSystemLibrary”里面的 RayTrace 接口。
KismetSystemLibrary的RayTrace接口
-
UFUNCTION(BlueprintCallable, Category=
"Collision", meta=(bIgnoreSelf=
"true", WorldContext=
"WorldContextObject", AutoCreateRefTerm=
"ActorsToIgnore", DisplayName=
"LineTraceByChannel", AdvancedDisplay=
"TraceColor,TraceHitColor,DrawTime", Keywords=
"raycast"))
-
static bool LineTraceSingle(UObject* WorldContextObject, const FVector Start, const FVector End, ETraceTypeQuery TraceChannel, bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, FHitResult& OutHit, bool bIgnoreSelf, FLinearColor TraceColor = FLinearColor::Red, FLinearColor TraceHitColor = FLinearColor::Green, float DrawTime = 5.0f);
-
UFUNCTION(BlueprintCallable, Category=
"Collision", meta=(bIgnoreSelf=
"true", WorldContext=
"WorldContextObject", AutoCreateRefTerm=
"ActorsToIgnore", DisplayName =
"MultiLineTraceByChannel", AdvancedDisplay=
"TraceColor,TraceHitColor,DrawTime", Keywords=
"raycast"))
-
static bool LineTraceMulti(UObject* WorldContextObject, const FVector Start, const FVector End, ETraceTypeQuery TraceChannel, bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, TArray<FHitResult>& OutHits, bool bIgnoreSelf, FLinearColor TraceColor = FLinearColor::Red, FLinearColor TraceHitColor = FLinearColor::Green, float DrawTime = 5.0f);
-
UFUNCTION(BlueprintCallable, Category=
"Collision", meta=(bIgnoreSelf=
"true", WorldContext=
"WorldContextObject", AutoCreateRefTerm=
"ActorsToIgnore", DisplayName =
"LineTraceForObjects", AdvancedDisplay=
"TraceColor,TraceHitColor,DrawTime", Keywords=
"raycast"))
-
static bool LineTraceSingleForObjects(UObject* WorldContextObject, const FVector Start, const FVector End, const TArray<TEnumAsByte<EObjectTypeQuery> > & ObjectTypes, bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, FHitResult& OutHit, bool bIgnoreSelf, FLinearColor TraceColor = FLinearColor::Red, FLinearColor TraceHitColor = FLinearColor::Green, float DrawTime = 5.0f );
-
UFUNCTION(BlueprintCallable, Category=
"Collision", meta=(bIgnoreSelf=
"true", WorldContext=
"WorldContextObject", AutoCreateRefTerm=
"ActorsToIgnore", DisplayName =
"MultiLineTraceForObjects", AdvancedDisplay=
"TraceColor,TraceHitColor,DrawTime", Keywords=
"raycast"))
-
static bool LineTraceMultiForObjects(UObject* WorldContextObject, const FVector Start, const FVector End, const TArray<TEnumAsByte<EObjectTypeQuery> > & ObjectTypes, bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, TArray<FHitResult>& OutHits, bool bIgnoreSelf, FLinearColor TraceColor = FLinearColor::Red, FLinearColor TraceHitColor = FLinearColor::Green, float DrawTime = 5.0f);
-
UFUNCTION(BlueprintCallable, Category =
"Collision", meta = (bIgnoreSelf =
"true", WorldContext =
"WorldContextObject", AutoCreateRefTerm =
"ActorsToIgnore", DisplayName =
"LineTraceByProfile", AdvancedDisplay =
"TraceColor,TraceHitColor,DrawTime", Keywords =
"raycast"))
-
static bool LineTraceSingleByProfile(UObject* WorldContextObject, const FVector Start, const FVector End, FName ProfileName, bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, FHitResult& OutHit, bool bIgnoreSelf, FLinearColor TraceColor = FLinearColor::Red, FLinearColor TraceHitColor = FLinearColor::Green, float DrawTime = 5.0f);
-
UFUNCTION(BlueprintCallable, Category =
"Collision", meta = (bIgnoreSelf =
"true", WorldContext =
"WorldContextObject", AutoCreateRefTerm =
"ActorsToIgnore", DisplayName =
"MultiLineTraceByProfile", AdvancedDisplay =
"TraceColor,TraceHitColor,DrawTime", Keywords =
"raycast"))
-
static bool LineTraceMultiByProfile(UObject* WorldContextObject, const FVector Start, const FVector End, FName ProfileName, bool bTraceComplex, const TArray<AActor*>& ActorsToIgnore, EDrawDebugTrace::Type DrawDebugType, TArray<FHitResult>& OutHits, bool bIgnoreSelf, FLinearColor TraceColor = FLinearColor::Red, FLinearColor TraceHitColor = FLinearColor::Green, float DrawTime = 5.0f);
LineTraceSingle
光线如果与物体相交(与多个物体相交,则碰到相应光线通道第一个“Block”的物体就返回true, 同时碰撞结果存在 FHitResult& OutHit)
EDrawDebugTrace::Type DrawDebugType
光线碰撞通道的类型,也就是要检测的相应光线碰撞通道设置“Block”的物体
看看EDrawDebugTrace
-
UENUM(BlueprintType)
-
enum ETraceTypeQuery
-
{
-
TraceTypeQuery1 UMETA(Hidden),
-
TraceTypeQuery2 UMETA(Hidden),
-
TraceTypeQuery3 UMETA(Hidden),
-
TraceTypeQuery4 UMETA(Hidden),
-
TraceTypeQuery5 UMETA(Hidden),
-
TraceTypeQuery6 UMETA(Hidden),
-
TraceTypeQuery7 UMETA(Hidden),
-
TraceTypeQuery8 UMETA(Hidden),
-
TraceTypeQuery9 UMETA(Hidden),
-
TraceTypeQuery10 UMETA(Hidden),
-
TraceTypeQuery11 UMETA(Hidden),
-
TraceTypeQuery12 UMETA(Hidden),
-
TraceTypeQuery13 UMETA(Hidden),
-
TraceTypeQuery14 UMETA(Hidden),
-
TraceTypeQuery15 UMETA(Hidden),
-
TraceTypeQuery16 UMETA(Hidden),
-
TraceTypeQuery17 UMETA(Hidden),
-
TraceTypeQuery18 UMETA(Hidden),
-
TraceTypeQuery19 UMETA(Hidden),
-
TraceTypeQuery20 UMETA(Hidden),
-
TraceTypeQuery21 UMETA(Hidden),
-
TraceTypeQuery22 UMETA(Hidden),
-
TraceTypeQuery23 UMETA(Hidden),
-
TraceTypeQuery24 UMETA(Hidden),
-
TraceTypeQuery25 UMETA(Hidden),
-
TraceTypeQuery26 UMETA(Hidden),
-
TraceTypeQuery27 UMETA(Hidden),
-
TraceTypeQuery28 UMETA(Hidden),
-
TraceTypeQuery29 UMETA(Hidden),
-
TraceTypeQuery30 UMETA(Hidden),
-
TraceTypeQuery31 UMETA(Hidden),
-
TraceTypeQuery32 UMETA(Hidden),
-
-
TraceTypeQuery_MAX UMETA(Hidden)
-
};
毫无疑问,定义了32个光线通道,这里 TraceTypeQuery_MAX 不算
这里的32个光线通道对应什么呢?看看UPrimitiveComponent组件的 Collision 设定:
默认之下存在两个光线通道 “Visibility” 和 “Camera”,
其中“Ignore” 代表了忽视,“Overlap” 代表了覆盖, “Block” 代表了阻止
Visibility 对应于上面 ETraceTypeQuery 中的 “TraceTypeQuery1”, Camera 对应于 “TraceTypeQuery2”
我们在 Engine->Collision 自己增加其他30个自定义光线通道, 如下图, 我们增加 "Test" 和 “AABB” 两个光线通道, 则按顺序分别对应于 “TraceTypeQuery3”, “TraceTypeQuery4”。以此类推剩下的自定义光线通道对应的ETraceTypeQuery枚举值。
下面演示代码,求角色对面正向光线检测的结果,检测通道为 “Visiblity”, ETraceTypeQuery::TraceTypeQuery1
-
void AMyProject4Character::Tick(float DeltaTime)
-
{
-
Super::Tick(DeltaTime);
-
-
const float TRACE_LINE_LENGTH =
1000.0f;
-
FRotator actorRotator = GetActorRotation();
-
const FRotator YawRotation(
0, actorRotator.Yaw,
0);
-
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
-
FVector lineStartPos = GetActorLocation();
-
FVector lineEndPos = lineStartPos + TRACE_LINE_LENGTH * Direction;
-
TArray<AActor*> arrayIgnoreActor;
-
FHitResult outResult;
-
if (UKismetSystemLibrary::LineTraceSingle(GetWorld(), lineStartPos, lineEndPos, ETraceTypeQuery::TraceTypeQuery1,
true, arrayIgnoreActor, EDrawDebugTrace::ForDuration, outResult,
true, FLinearColor::Red, FLinearColor::Green,
0.1f))
-
{
-
FString locationString = outResult.Location.ToString();
-
UE_LOG(LogTemp, Error, TEXT(
"location = %s"), *locationString);
-
}
-
-
}
下面图中一个物体的"Visiblity"通道为Overlap, 一个物体的“Visiblity”为Block
检测结果:
LineTraceMulti
和LineTraceSingle差不多,就是光线检测结果的数量不一样,LineTraceSingle碰到第一个光线“Block”物体就返回true,也就是如果从没碰到过“Block”则返回false, LineTraceMulti把所有相交的物体结果(经过的“OverLap” 物体和最后的“Block” 物体)都存入 FHitResult 数组,
看看下面的设置:
-
TArray<FHitResult> outResults;
-
if (UKismetSystemLibrary::LineTraceMulti(GetWorld(), lineStartPos, lineEndPos, ETraceTypeQuery::TraceTypeQuery1,
true, arrayIgnoreActor, EDrawDebugTrace::ForDuration, outResults,
true, FLinearColor::Red, FLinearColor::Green,
0.1f))
-
{
-
for (int resultIndex =
0; resultIndex < outResults.Num(); ++resultIndex)
-
{
-
FString locationString = outResults[resultIndex].Location.ToString();
-
UE_LOG(LogTemp, Error, TEXT(
"%d location = %s"), resultIndex+
1, *locationString);
-
}
-
}
运行结果:
(1)射线先从 “Overlap” Visiblity的物体进入:
(2)射线先从 “Block” Visiblity的物体进入:
LineTraceSingleForObjects
光线与对象类型数组内的任意一个对象类型的物体碰撞获取碰撞结果
TArray<TEnumAsByte<EObjectTypeQuery>> & ObjectTypes
解释下 “EObjectTypeQuery”,看看UPrimitiveComponent组件的碰撞设置:
UE4系统默认为我们定义了 “WorldStatic”, “WorldDynamic”, "Pawn", "PhysicsBody", "Vehicle", "Destructible" 六个ObjectType,
我们的每个UPrimitiveComponent都有一个对象类型,在回到 EObjectTypeQuery 枚举看看源码:
-
UENUM(BlueprintType)
-
enum EObjectTypeQuery
-
{
-
ObjectTypeQuery1 UMETA(Hidden),
-
ObjectTypeQuery2 UMETA(Hidden),
-
ObjectTypeQuery3 UMETA(Hidden),
-
ObjectTypeQuery4 UMETA(Hidden),
-
ObjectTypeQuery5 UMETA(Hidden),
-
ObjectTypeQuery6 UMETA(Hidden),
-
ObjectTypeQuery7 UMETA(Hidden),
-
ObjectTypeQuery8 UMETA(Hidden),
-
ObjectTypeQuery9 UMETA(Hidden),
-
ObjectTypeQuery10 UMETA(Hidden),
-
ObjectTypeQuery11 UMETA(Hidden),
-
ObjectTypeQuery12 UMETA(Hidden),
-
ObjectTypeQuery13 UMETA(Hidden),
-
ObjectTypeQuery14 UMETA(Hidden),
-
ObjectTypeQuery15 UMETA(Hidden),
-
ObjectTypeQuery16 UMETA(Hidden),
-
ObjectTypeQuery17 UMETA(Hidden),
-
ObjectTypeQuery18 UMETA(Hidden),
-
ObjectTypeQuery19 UMETA(Hidden),
-
ObjectTypeQuery20 UMETA(Hidden),
-
ObjectTypeQuery21 UMETA(Hidden),
-
ObjectTypeQuery22 UMETA(Hidden),
-
ObjectTypeQuery23 UMETA(Hidden),
-
ObjectTypeQuery24 UMETA(Hidden),
-
ObjectTypeQuery25 UMETA(Hidden),
-
ObjectTypeQuery26 UMETA(Hidden),
-
ObjectTypeQuery27 UMETA(Hidden),
-
ObjectTypeQuery28 UMETA(Hidden),
-
ObjectTypeQuery29 UMETA(Hidden),
-
ObjectTypeQuery30 UMETA(Hidden),
-
ObjectTypeQuery31 UMETA(Hidden),
-
ObjectTypeQuery32 UMETA(Hidden),
-
-
ObjectTypeQuery_MAX UMETA(Hidden)
-
};
其中“WorldStatic”对应于 ObjectTypeQuery1 , “WorldDynamic” 对应于 ObjectTypeQuery2,。。。。。。"Destructible"对应于ObjectTypeQuery6.
我们可以在Engine->Collision自定义新增加 ObjectType, 如所示:
新增加的“TestObject” 就是ObjectTypeQuery7,依次往下推断
看示例demo:
我们仅仅检测“WorldStatic" 对象:
-
TArray<TEnumAsByte<EObjectTypeQuery>> arrayObjectTypeQuery;
-
arrayObjectTypeQuery.Add(EObjectTypeQuery::ObjectTypeQuery1);
-
if (UKismetSystemLibrary::LineTraceSingleForObjects(GetWorld(), lineStartPos, lineEndPos, arrayObjectTypeQuery,
true, arrayIgnoreActor, EDrawDebugTrace::ForDuration, outResult,
true, FLinearColor::Red, FLinearColor::Green,
0.1f))
-
{
-
FString locationString = outResult.Location.ToString();
-
UE_LOG(LogTemp, Error, TEXT(
"location is %s"), *locationString);
-
}
LineTraceMultiForObjects
LineTraceMultiForObjects 和 LineTraceSingleForObjects 差不多,就是求多个光线相交对象。
LineTraceSingleByProfile
跟着碰撞描述定义的对象碰撞来进行光线求交,选取的对象范围比前面的“LineTraceSingle”的光线通道Block 和 “LineTraceSingleForObjects” 的“ObjectType” 都要广。遇上Profile下的碰撞对象类型为“Block”返回结果。
FName ProfileName
碰撞描述名字,看UPrimitiveComponent的碰撞设定,如下所示:
这些"NoCollision”, "BlockAll"等等就是CollisionProfile,一个CollisionProfile定义了一个UPrimitiveComponent对各种光线,各种对象的碰撞情况,这些 CollisionProfile 在 项目名/Config/DefaultEngine.ini 配置文件可以看到,下面对应上面那张图的DefaultEngine.ini
拿“BlockAll”来说, 看下面:
BlockAll 规定了 UPrimitiveComponent 对各种光线通道和各种对象物体都是Block的状态,
我们尝试新增加一个名为“‘CustomAA” 的CollisionProfile, 如下所示:
除了“WorldDynamic” 和 “Pawn”是 Overlap, 所有其他对象都是Block, 看下面的设定
-
FName profileName = FName(
"CustomAA");
-
if (UKismetSystemLibrary::LineTraceSingleByProfile(GetWorld(), lineStartPos, lineEndPos, profileName,
true, arrayIgnoreActor, EDrawDebugTrace::ForDuration, outResult,
true, FLinearColor::Red, FLinearColor::Green,
0.1f))
-
{
-
FString locationString = outResult.Location.ToString();
-
UE_LOG(LogTemp,
运行结果:
LineTraceMultiByProfile
LineTraceMultiByProfile其实就是碰上第一个Block对象就终止,经过的"Overlap"对象和最终的Block对象存入FHitResult结果数组。如果从来没有碰到过“Block”对象,则返回false,反之返回true
WorldCollision.h
实际上 KismetSystemLibrary 的 raytrace 接口都是封装 WorldCollision.h 的各种raytrace接口,如下所示,有时候如果KismetSystemLibrary.h的 raytrace 接口不是很好的满足我们的需求的时候,可以直接 拿WorldCollision.h 的各种raytrace接口来使用。
-
bool LineTraceSingleByChannel(struct FHitResult& OutHit,const FVector& Start,const FVector& End,ECollisionChannel TraceChannel,const FCollisionQueryParams& Params = FCollisionQueryParams::DefaultQueryParam, const FCollisionResponseParams& ResponseParam = FCollisionResponseParams::DefaultResponseParam) const;
-
-
bool LineTraceSingleByObjectType(struct FHitResult& OutHit,const FVector& Start,const FVector& End,const FCollisionObjectQueryParams& ObjectQueryParams, const FCollisionQueryParams& Params = FCollisionQueryParams::DefaultQueryParam) const;
-
bool LineTraceSingleByProfile(struct FHitResult& OutHit, const FVector& Start, const FVector& End, FName ProfileName, const FCollisionQueryParams& Params = FCollisionQueryParams::DefaultQueryParam) const;
bool LineTraceMultiByChannel(TArray<struct FHitResult>& OutHits,const FVector& Start,const FVector& End,ECollisionChannel TraceChannel,const FCollisionQueryParams& Params = FCollisionQueryParams::DefaultQueryParam, const FCollisionResponseParams& ResponseParam = FCollisionResponseParams::DefaultResponseParam) const;
bool LineTraceMultiByObjectType(TArray<struct FHitResult>& OutHits,const FVector& Start,const FVector& End,const FCollisionObjectQueryParams& ObjectQueryParams, const FCollisionQueryParams& Params = FCollisionQueryParams::DefaultQueryParam) const;
bool LineTraceMultiByProfile(TArray<struct FHitResult>& OutHits, const FVector& Start, const FVector& End, FName ProfileName, const FCollisionQueryParams& Params = FCollisionQueryParams::DefaultQueryParam) const;
LineTraceSingleByChannel
根据碰撞面板获取光线碰撞对象
ECollisionChannel TraceChannel
看看ECollisionChannel枚举的值:
-
UENUM(BlueprintType)
-
enum ECollisionChannel
-
{
-
-
ECC_WorldStatic UMETA(DisplayName="WorldStatic"),
-
ECC_WorldDynamic UMETA(DisplayName="WorldDynamic"),
-
ECC_Pawn UMETA(DisplayName="Pawn"),
-
ECC_Visibility UMETA(DisplayName="Visibility" , TraceQuery="1"),
-
ECC_Camera UMETA(DisplayName="Camera" , TraceQuery="1"),
-
ECC_PhysicsBody UMETA(DisplayName="PhysicsBody"),
-
ECC_Vehicle UMETA(DisplayName="Vehicle"),
-
ECC_Destructible UMETA(DisplayName="Destructible"),
-
-
/** Reserved for gizmo collision */
-
ECC_EngineTraceChannel1 UMETA(Hidden),
-
-
ECC_EngineTraceChannel2 UMETA(Hidden),
-
ECC_EngineTraceChannel3 UMETA(Hidden),
-
ECC_EngineTraceChannel4 UMETA(Hidden),
-
ECC_EngineTraceChannel5 UMETA(Hidden),
-
ECC_EngineTraceChannel6 UMETA(Hidden),
-
-
ECC_GameTraceChannel1 UMETA(Hidden),
-
ECC_GameTraceChannel2 UMETA(Hidden),
-
ECC_GameTraceChannel3 UMETA(Hidden),
-
ECC_GameTraceChannel4 UMETA(Hidden),
-
ECC_GameTraceChannel5 UMETA(Hidden),
-
ECC_GameTraceChannel6 UMETA(Hidden),
-
ECC_GameTraceChannel7 UMETA(Hidden),
-
ECC_GameTraceChannel8 UMETA(Hidden),
-
ECC_GameTraceChannel9 UMETA(Hidden),
-
ECC_GameTraceChannel10 UMETA(Hidden),
-
ECC_GameTraceChannel11 UMETA(Hidden),
-
ECC_GameTraceChannel12 UMETA(Hidden),
-
ECC_GameTraceChannel13 UMETA(Hidden),
-
ECC_GameTraceChannel14 UMETA(Hidden),
-
ECC_GameTraceChannel15 UMETA(Hidden),
-
ECC_GameTraceChannel16 UMETA(Hidden),
-
ECC_GameTraceChannel17 UMETA(Hidden),
-
ECC_GameTraceChannel18 UMETA(Hidden),
-
-
/** Add new serializeable channels above here (i.e. entries that exist in FCollisionResponseContainer) */
-
/** Add only nonserialized/transient flags below */
-
-
// NOTE!!!! THESE ARE BEING DEPRECATED BUT STILL THERE FOR BLUEPRINT. PLEASE DO NOT USE THEM IN CODE
-
-
ECC_OverlapAll_Deprecated UMETA(Hidden),
-
ECC_MAX,
-
};
ECC_WorldStatic,ECC_WorldDynamic等等代表了对象通道,而ECC_Visibility 和 ECC_Camera 代表了光线通道。
ECC_GameTraceChannel1 - ECC_GameTraceChannel18 为新增加自定义通道(对象或者光线通道)
Config/DefaultEngine.ini
由于 KismetSystemLibrary的LineTraceSingle 封装的是LineTraceSingleByChannel,看源码,其中
ETraceTypeQuery 和 ECollisionChannel的转换关系如下所示, 有兴趣自己继续往下翻查看源码
ECollisionChannel CollisionChannel = UEngineTypes::ConvertToCollisionChannel(TraceChannel);
解决射线与UPrimitiveComponent只相交于正面的问题
在UE4里的各种LineTrace接口获取与一个UPrimitiveComponent的交点,是位于光线方向面对UPrimitiveComponent的点,如果我们想获取射线与UPrimitiveComponent背面的交点怎么办?就是把光线起点FVector Start 和 光线终点FVector End对调。所以如果你同时想获取射线与UPrimitiveComponent正面以及背面的方法,我想到的是正反都发射一条射线。