UE4移动-FHitResult详解

背景

碰撞一般都是通过Trace/Sweep检测, 有了它作为基础, 才能实现移动的具体操作.

Sweep Capsule下的FHitResult

构造测试环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
void ABasePlayerController::TestDoCapsuleTrace(float StartZOffset, float EndZOffset, float InDistance)
{
float DuringTime = 50.f;
ACharacter *pChar = GetCharacter();
FVector StartLocation = pChar->GetActorLocation();

FVector Forward = pChar->GetActorForwardVector().GetSafeNormal();
FVector Right = pChar->GetActorRightVector().GetSafeNormal();
StartLocation.Z += StartZOffset;
FVector EndLocation = StartLocation + Forward*InDistance;
EndLocation.Z += EndZOffset;

float ScaledCapsuleHalfHeight = pChar->GetCapsuleComponent()->GetScaledCapsuleHalfHeight();
float ScaledCapsuleRadius = pChar->GetCapsuleComponent()->GetScaledCapsuleRadius();

FCollisionQueryParams Params;
Params.AddIgnoredActor(pChar);
FName ProfilerName("MyTestProfiler");

UKismetSystemLibrary::DrawDebugLine(this, StartLocation, EndLocation, FLinearColor::Red, DuringTime);
UKismetSystemLibrary::DrawDebugCapsule(this, StartLocation, ScaledCapsuleHalfHeight, ScaledCapsuleRadius, FRotator::ZeroRotator, FLinearColor::Blue, DuringTime);
UKismetSystemLibrary::DrawDebugBox(this, StartLocation, FVector::OneVector, FLinearColor::Blue, FRotator::ZeroRotator, DuringTime);
UKismetSystemLibrary::DrawDebugCapsule(this, EndLocation, ScaledCapsuleHalfHeight, ScaledCapsuleRadius, FRotator::ZeroRotator, FLinearColor::Blue, DuringTime);
UKismetSystemLibrary::DrawDebugBox(this, EndLocation, FVector::OneVector, FLinearColor::Blue, FRotator::ZeroRotator, DuringTime);


FHitResult HitResult;
bool bHit = GetWorld()->SweepSingleByProfile(HitResult, StartLocation, EndLocation, FQuat::Identity, ProfilerName, FCollisionShape::MakeCapsule(ScaledCapsuleRadius, ScaledCapsuleHalfHeight), Params);
if (bHit)
{
if (HitResult.bBlockingHit)
{
// Location box
UKismetSystemLibrary::DrawDebugBox(this, HitResult.Location, FVector::OneVector, FLinearColor::Blue, FRotator::ZeroRotator, DuringTime);

// impact point box
UKismetSystemLibrary::DrawDebugBox(this, HitResult.ImpactPoint, FVector::OneVector, FLinearColor::Blue, FRotator::ZeroRotator, DuringTime);

FVector BottomLocation = HitResult.Location;
BottomLocation.Z = HitResult.Location.Z - ScaledCapsuleHalfHeight + ScaledCapsuleRadius;
UKismetSystemLibrary::DrawDebugBox(this, BottomLocation, FVector::OneVector, FLinearColor::Blue, FRotator::ZeroRotator, DuringTime);
UKismetSystemLibrary::DrawDebugLine(this, HitResult.Location, BottomLocation, FLinearColor::Green, DuringTime);



// draw hit
UKismetSystemLibrary::DrawDebugCapsule(this, HitResult.Location, ScaledCapsuleHalfHeight, ScaledCapsuleRadius, FRotator::ZeroRotator, FLinearColor::Red, DuringTime);

// hit point+impact normal
UKismetSystemLibrary::DrawDebugLine(this, HitResult.ImpactPoint, HitResult.ImpactPoint + HitResult.ImpactNormal * 100, FLinearColor::Green, DuringTime);

// hit point+ normal
UKismetSystemLibrary::DrawDebugLine(this, HitResult.ImpactPoint, HitResult.ImpactPoint+ HitResult.Normal*100, FLinearColor::White, DuringTime);

}
else
{

}
}
}

测试结果如图:

FHitResult.Time

\(Time=\frac{FHitResult.Distance}{TraceEnd-TraceStart}\)

1
2
3
4
5
6
/**
* 'Time' of impact along trace direction (ranging from 0.0 to 1.0) if there is a hit, indicating time between TraceStart and TraceEnd.
* For swept movement (but not queries) this may be pulled back slightly from the actual time of impact, to prevent precision problems with adjacent geometry.
*/
UPROPERTY()
float Time;

FHitResult.Distance

从trace起始点到碰撞点(还是胶囊体位置)的距离.

1
2
3
/** The distance from the TraceStart to the Location in world space. This value is 0 if there was an initial overlap (trace started inside another colliding object). */
UPROPERTY()
float Distance;

FHitResult.Location

当没有bStartPenetrating时为: 碰撞时胶囊体所在位置. 当有bStartPenetrating, FHitResult.LocationFHitResult.TraceStart相等.

当有bStartPenetrating, FHitResult.LocationFHitResult.TraceStart相等.

1
2
3
4
5
6
7
/**
* The location in world space where the moving shape would end up against the impacted object, if there is a hit. Equal to the point of impact for line tests.
* Example: for a sphere trace test, this is the point where the center of the sphere would be located when it touched the other object.
* For swept movement (but not queries) this may not equal the final location of the shape since hits are pulled back slightly to prevent precision issues from overlapping another surface.
*/
UPROPERTY()
FVector_NetQuantize Location;

FHitResult.ImpactPoint

碰撞点位置

1
2
3
4
5
6
7
/**
* Location in world space of the actual contact of the trace shape (box, sphere, ray, etc) with the impacted object.
* Example: for a sphere trace test, this is the point where the surface of the sphere touches the other object.
* @note: In the case of initial overlap (bStartPenetrating=true), ImpactPoint will be the same as Location because there is no meaningful single impact point to report.
*/
UPROPERTY()
FVector_NetQuantize ImpactPoint;

FHitResult.Normal

FHitResult.Normal: 碰撞体(胶囊体)在碰撞点切面的法线. 可以看出, 当胶囊体下半球与物体相撞时候, FHitResult.Normal过下半球的球心; 当胶囊体中间的圆柱部分与物体相撞时候, FHitResult.Normal与圆柱的轴线相交.

1
2
3
4
5
6
7
/**
* Normal of the hit in world space, for the object that was swept. Equal to ImpactNormal for line tests.
* This is computed for capsules and spheres, otherwise it will be the same as ImpactNormal.
* Example: for a sphere trace test, this is a normalized vector pointing in towards the center of the sphere at the point of impact.
*/
UPROPERTY()
FVector_NetQuantizeNormal Normal;

FHitResult.ImpactNormal

FHitResult.ImpactNormal: 被碰撞物体在碰撞点切面的法线. 注意当发生Penetrating时, FHitResult.ImpactNormal等于FHitResult.Normal, 他们都是碰撞体表面法线.

1
2
3
4
5
6
7
/**
* Normal of the hit in world space, for the object that was hit by the sweep, if any.
* For example if a sphere hits a flat plane, this is a normalized vector pointing out from the plane.
* In the case of impact with a corner or edge of a surface, usually the "most opposing" normal (opposed to the query direction) is chosen.
*/
UPROPERTY()
FVector_NetQuantizeNormal ImpactNormal;

FHitResult.TraceStart

碰撞检测的起始位置.

1
2
3
4
5
6
/**
* Start location of the trace.
* For example if a sphere is swept against the world, this is the starting location of the center of the sphere.
*/
UPROPERTY()
FVector_NetQuantize TraceStart;

FHitResult.TraceEnd

碰撞检测的结束位置.

1
2
3
4
5
6
/**
* End location of the trace; this is NOT where the impact occurred (if any), but the furthest point in the attempted sweep.
* For example if a sphere is swept against the world, this would be the center of the sphere if there was no blocking hit.
*/
UPROPERTY()
FVector_NetQuantize TraceEnd;

FHitResult.PenetrationDepth

如果碰撞的起始位置在某个物体内部, 则沿着Normal方向移动PenetrationDepth距离, 可以最快脱离碰撞体. 即: 脱离的最短距离为FHitResult.PenetrationDepth.

1
2
3
4
5
6
7
/**
* If this test started in penetration (bStartPenetrating is true) and a depenetration vector can be computed,
* this value is the distance along Normal that will result in moving out of penetration.
* If the distance cannot be computed, this distance will be zero.
*/
UPROPERTY()
float PenetrationDepth;

FHitResult.bStartPenetrating

是否发生渗透现象, 即碰撞起始点是否在物体内部.

1
2
3
4
5
6
7
8
/**
* Whether the trace started in penetration, i.e. with an initial blocking overlap.
* In the case of penetration, if PenetrationDepth > 0.f, then it will represent the distance along the Normal vector that will result in
* minimal contact between the swept shape and the object that was hit. In this case, ImpactNormal will be the normal opposed to movement at that location
* (ie, Normal may not equal ImpactNormal). ImpactPoint will be the same as Location, since there is no single impact point to report.
*/
UPROPERTY()
uint8 bStartPenetrating : 1;

FHitResult.bBlockingHit

是否发生了Block类型的碰撞, 如果为false则表示发生了Overlap.

1
2
3
/** Indicates if this hit was a result of blocking collision. If false, there was no hit or it was an overlap/touch instead. */
UPROPERTY()
uint8 bBlockingHit : 1;

FHitResult.PhysMaterial

1
2
3
4
5
6
/**
* Physical material that was hit.
* @note Must set bReturnPhysicalMaterial on the swept PrimitiveComponent or in the query params for this to be returned.
*/
UPROPERTY()
TWeakObjectPtr<class UPhysicalMaterial> PhysMaterial;

FHitResult.Actor

被碰撞物体.

1
2
3
/** Actor hit by the trace. */
UPROPERTY()
TWeakObjectPtr<class AActor> Actor;

FHitResult.Component

被碰撞的Component

1
2
3
/** PrimitiveComponent hit by the trace. */
UPROPERTY()
TWeakObjectPtr<class UPrimitiveComponent> Component;

FHitResult.BoneName

1
2
3
/** Name of bone we hit (for skeletal meshes). */
UPROPERTY()
FName BoneName;

FHitResult.MyBoneName

1
2
3
/** Name of the _my_ bone which took part in hit event (in case of two skeletal meshes colliding). */
UPROPERTY()
FName MyBoneName;