在 Unreal Engine 中,Sweeping 是一个用于检测物体碰撞的技术,它可以在物体移动时检测它沿着移动路径上是否与其他物体发生碰撞。它主要用于防止物体在移动过程中穿透其他物体,确保物体的移动和碰撞检测更准确。
1. Sweeping 的基本概念
当你在游戏中移动一个物体时,如果直接设置物体的新位置而不进行碰撞检测,物体可能会穿过其他物体(比如墙壁、地板等)。为了防止这种情况发生,可以使用 Sweeping 来在移动物体时检测其沿路径上的碰撞。
Sweeping 的工作原理如下:
- 当一个物体从一个位置移动到另一个位置时,
Sweep
会检测这个物体在它移动路径上的所有位置,寻找任何可能与它发生碰撞的物体。 - 如果在移动过程中检测到碰撞,
Sweep
会返回详细的碰撞信息(如碰撞点、法线、碰撞的物体等)。 Sweep
还会将移动过程中的物体放置在碰撞位置之前,以避免穿透。
2. Sweep
的使用场景
- 角色移动:角色在行走时检测前方是否有障碍物,防止穿墙或穿地。
- 物体运动:检测移动物体(如子弹、飞行物体)是否与其他物体发生碰撞。
- 环境交互:例如,门在开关过程中,检测是否有物体挡住了门。
3. 使用 Sweep
的常见函数
Unreal Engine 提供了几种 Sweep
的方法和函数,以下是一些常用的方法:
3.1 AddActorWorldOffset
bool AActor::AddActorWorldOffset(FVector DeltaLocation, bool bSweep, FHitResult* OutHit, ETeleportType Teleport)
DeltaLocation
:相对于当前世界位置的偏移量。bSweep
:如果为true
,则在移动时进行Sweep
检测。OutHit
:如果发生碰撞,该结构体将存储碰撞的信息。Teleport
:是否瞬移而不触发事件。
示例:
FHitResult Hit; FVector MoveDelta = FVector(100, 0, 0); // 向前移动 100 单位 AddActorWorldOffset(MoveDelta, true, &Hit); if (Hit.IsValidBlockingHit()) { // 如果检测到碰撞,处理碰撞逻辑 UE_LOG(LogTemp, Warning, TEXT("Hit something: %s"), *Hit.GetActor()->GetName()); }
3.2 SetActorLocation
cpp
复制代码
bool AActor::SetActorLocation(FVector NewLocation, bool bSweep, FHitResult* OutHit, ETeleportType Teleport)
NewLocation
:目标位置。bSweep
:如果为true
,在移动到新位置时进行Sweep
检测。OutHit
:如果发生碰撞,该结构体将存储碰撞的信息。Teleport
:是否瞬移而不触发事件。
示例:
FHitResult Hit; FVector TargetLocation = GetActorLocation() + FVector(100, 0, 0); SetActorLocation(TargetLocation, true, &Hit); if (Hit.IsValidBlockingHit()) { // 如果检测到碰撞,处理碰撞逻辑 UE_LOG(LogTemp, Warning, TEXT("Hit something: %s"), *Hit.GetActor()->GetName()); }
3.3 SweepSingleByChannel
SweepSingleByChannel
是用于进行复杂碰撞检测的函数,它允许你指定一个通道来进行检测,并返回碰撞的详细信息。
bool UWorld::SweepSingleByChannel( FHitResult& OutHit, const FVector& Start, const FVector& End, const FQuat& Rot, ECollisionChannel TraceChannel, const FCollisionShape& CollisionShape, const FCollisionQueryParams& Params = FCollisionQueryParams::DefaultQueryParam, const FCollisionResponseParams& ResponseParam = FCollisionResponseParams::DefaultResponseParam )
OutHit
:存储碰撞的详细信息。Start
和End
:检测的起点和终点。Rot
:检测时使用的旋转。TraceChannel
:用于检测的碰撞通道。CollisionShape
:检测时使用的碰撞形状(如球体、盒子、胶囊体等)。
示例:
FHitResult Hit; FVector Start = GetActorLocation(); FVector End = Start + FVector(100, 0, 0); // 向前移动 100 单位 // 定义一个碰撞形状(球体) FCollisionShape CollisionShape; CollisionShape.SetSphere(50.0f); // 设置球体半径 // 执行 Sweep 检测 bool bHit = GetWorld()->SweepSingleByChannel( Hit, Start, End, FQuat::Identity, // 不旋转 ECC_Visibility, // 使用 Visibility 通道 CollisionShape ); if (bHit) { // 处理碰撞 UE_LOG(LogTemp, Warning, TEXT("Hit something: %s"), *Hit.GetActor()->GetName()); }
4. Sweep
检测的结果
当 Sweep
检测发生碰撞时,返回的 FHitResult
结构体会包含以下信息:
HitActor
:碰撞到的物体。ImpactPoint
:碰撞发生的位置。ImpactNormal
:碰撞面的法线方向。bBlockingHit
:是否是阻挡性的碰撞。Time
:碰撞发生的时间(相对于移动的进程,0 到 1 之间的值)。
5. 使用 Sweep
的注意事项
-
碰撞体设置:
- 确保参与
Sweep
的物体有正确的碰撞体和碰撞通道设置。否则,Sweep
无法检测到碰撞。
- 确保参与
-
Sweep
的性能问题:Sweep
检测比简单的SetActorLocation
更耗费性能,尤其是检测复杂形状时。因此在频繁调用Sweep
时需要谨慎,避免性能下降。
-
旋转的影响:
- 如果物体在
Sweep
检测中包含旋转(如SweepSingleByChannel
中的FQuat
),检测结果会受到影响,要注意检测方向和形状的关系。
- 如果物体在
6. 蓝图中使用 Sweep
在蓝图中,Sweep
相关节点通常用于检测物体移动过程中的碰撞:
- Set Actor Location (Sweep):设置物体位置时进行
Sweep
检测。 - Add Actor World Offset (Sweep):相对于当前位置移动物体时进行
Sweep
检测。
7. 总结
Sweep
是一种在物体移动时进行碰撞检测的方法,用于防止物体穿透其他物体。- 可以使用
SetActorLocation
、AddActorWorldOffset
、SweepSingleByChannel
等函数进行Sweep
检测。 - 检测结果存储在
FHitResult
中,包含碰撞物体、碰撞位置、碰撞法线等信息。 - 在使用
Sweep
时要注意碰撞体的设置和检测的通道,确保检测到需要的碰撞。
1. 碰撞体的设置
碰撞体(Collision Component)定义了物体的物理边界,用于检测物体之间的碰撞和重叠。常见的碰撞体组件有:
- Box Component(盒体碰撞)
- Sphere Component(球体碰撞)
- Capsule Component(胶囊体碰撞)
- Mesh Component(网格碰撞,可以是简单或复杂的网格形状)
1.1 在 C++ 中设置碰撞体
在 C++ 代码中,可以为 Actor 或 Component 设置碰撞体。以下是一个设置胶囊体碰撞的例子:
#include "Components/CapsuleComponent.h" // 在构造函数中创建并初始化碰撞体 AMyActor::AMyActor() { // 创建胶囊体碰撞组件 CapsuleComponent = CreateDefaultSubobject<UCapsuleComponent>(TEXT("CapsuleComponent")); CapsuleComponent->InitCapsuleSize(42.f, 96.f); // 设置胶囊体半径和高度 CapsuleComponent->SetCollisionProfileName(TEXT("Pawn")); // 设置碰撞配置 // 将胶囊体设为根组件 RootComponent = CapsuleComponent; }
1.2 在蓝图中设置碰撞体
-
选择 Actor 或 Component:
- 打开需要设置碰撞体的
Actor
或Component
蓝图。 - 在
Components
面板中选择需要设置碰撞的组件(例如StaticMeshComponent
、BoxComponent
等)。
- 打开需要设置碰撞体的
-
设置碰撞属性:
- 在
Details
面板中,找到Collision
分类。 - 设置
Collision Presets
为适当的预设(例如BlockAll
、OverlapAll
、NoCollision
等)。 - 如果需要自定义碰撞,可以点击
Custom
,然后单独设置Collision Enabled
、Object Type
和Collision Responses
。
- 在
-
调整碰撞体形状和大小:
- 对于基本碰撞组件(例如盒体、球体、胶囊体),可以在
Details
面板中设置它们的大小、半径和高度。 - 对于网格组件(如
StaticMeshComponent
),可以设置Collision Complexity
来选择简单碰撞(使用基本形状)或复杂碰撞(使用网格的精确形状)。
- 对于基本碰撞组件(例如盒体、球体、胶囊体),可以在
2. 碰撞通道(Collision Channels)
碰撞通道定义了不同物体之间的碰撞响应关系。每个物体都有一个 Object Type
和一个 Collision Response
,这决定了它与其他物体的交互方式。
2.1 Object Type(对象类型)
Object Type
定义了物体的类型,例如 WorldStatic
、WorldDynamic
、Pawn
、Vehicle
等。这些类型用于标识物体在碰撞检测中扮演的角色。
2.2 Collision Response(碰撞响应)
Collision Response
定义了物体对其他通道的反应方式,有以下几种响应类型:
- Block(阻挡):物体将阻挡其他物体,并发生物理碰撞。
- Overlap(重叠):物体将与其他物体重叠,但不会发生物理碰撞。
- Ignore(忽略):物体将忽略其他物体,不会发生任何碰撞或重叠事件。
2.3 在 C++ 中设置碰撞通道
#include "Components/BoxComponent.h" // 在构造函数中创建并初始化碰撞体 AMyActor::AMyActor() { // 创建盒体碰撞组件 BoxComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxComponent")); BoxComponent->SetBoxExtent(FVector(50.f, 50.f, 50.f)); // 设置盒体大小 // 设置碰撞响应配置 BoxComponent->SetCollisionProfileName(TEXT("Custom")); BoxComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); // 启用碰撞检测和物理模拟 BoxComponent->SetCollisionObjectType(ECC_WorldDynamic); // 设置对象类型为动态物体 // 设置不同通道的碰撞响应 BoxComponent->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block); // 对静态物体阻挡 BoxComponent->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap); // 对 Pawn 重叠 BoxComponent->SetCollisionResponseToAllChannels(ECR_Ignore); // 对所有其他通道忽略 RootComponent = BoxComponent; }
4. 检测通道的使用
在进行 Line Trace
、Sweep
或 Overlap
检测时,可以指定使用的检测通道(Trace Channel
或 Collision Channel
),以便只检测特定类型的物体。
4.1 Line Trace 的使用
FHitResult HitResult; FVector Start = GetActorLocation(); FVector End = Start + FVector(1000, 0, 0); // 线段终点 FCollisionQueryParams Params; Params.AddIgnoredActor(this); // 忽略自己 // 使用 Visibility 通道进行检测 bool bHit = GetWorld()->LineTraceSingleByChannel(HitResult, Start, End, ECC_Visibility, Params); if (bHit) { UE_LOG(LogTemp, Warning, TEXT("Hit: %s"), *HitResult.GetActor()->GetName()); }
4.2 Sweep 的使用
FHitResult HitResult; FVector Start = GetActorLocation(); FVector End = Start + FVector(500, 0, 0); // 终点 FCollisionShape CollisionShape; CollisionShape.SetSphere(50.0f); // 设置碰撞形状(球体) // 使用 PhysicsBody 通道进行 Sweep 检测 bool bHit = GetWorld()->SweepSingleByChannel(HitResult, Start, End, FQuat::Identity, ECC_PhysicsBody, CollisionShape); if (bHit) { UE_LOG(LogTemp, Warning, TEXT("Hit: %s"), *HitResult.GetActor()->GetName()); }