关于 ue-physics-collision
面向 Unreal Engine 5.8 开发者的物理与碰撞技能,涵盖查询、碰撞事件、刚体和 Chaos 设置,并提供与当前头文件对应的 API 参考。
主要功能
- 01碰撞通道与配置文件
- 02射线、扫掠和重叠查询
- 03FHitResult 命中信息
- 04碰撞与唤醒事件
- 05刚体、力和物理材质
- 06Chaos 求解器与网络物理
适用场景
- 01配置对象与追踪通道
- 02实现命中、交互或范围检测
- 03响应组件重叠与碰撞
- 04设置刚体运动和物理参数
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ quodsoler/ unreal-engine-skills --skill ue-physics-collision安装后在 Claude Code 中输入/ue-physics-collision确认已加载。
附带文件references/collision-channel-setup.mdreferences/trace-patterns.md
详细介绍
UE 物理与碰撞
目标引擎:UE 5.8。以下 API 已根据 5.8 头文件验证;旧版形式列于“已弃用 — 请勿使用”。
碰撞和物理位于 Engine 模块(UPrimitiveComponent、UWorld 查询、FHitResult、UCollisionProfile)和 PhysicsCore 模块(FCollisionShape、UPhysicalMaterial、FBodyInstanceCore、ECollisionTraceFlag、EPhysicalSurface)。模拟器本身是 Chaos(Chaos / ChaosSolverEngine 模块);破坏功能还需要 GeometryCollectionEngine 和 FieldSystemEngine。大多数游戏代码只需在 Build.cs 中添加 "Engine" 和 "PhysicsCore"。
上下文
如果 .agents/ue-project-context.md 存在,请阅读该文件(模块名称、约定、已启用插件、GAS/网络设置)。如果文件不存在,也请继续。
根据请求和代码库确定相关领域。只有在两种合理理解会导致不同代码时才提问。
| 请求涉及…… | 前往 |
|---|---|
通道、配置文件、DefaultEngine.ini、响应 | 碰撞通道和配置文件 |
| 线迹、扫掠、重叠查询、异步线迹 | 线迹、扫掠和重叠查询 |
| 读取查询命中信息 | FHitResult |
OnComponentHit、OnComponentBeginOverlap、唤醒/休眠 | 碰撞事件 |
| 刚体、冲量、力、质量、阻尼、休眠 | 物理体和力 |
| 约束、径向力场、布娃娃 | 约束、径向力和布娃娃 |
| 摩擦、恢复系数、表面类型 | 物理材质 |
| 求解器设置、子步进、CVar、物理线程刻度 | Chaos 求解器和异步物理 |
| 复制物理、重新模拟、预测插值 | 网络物理 |
碰撞通道和配置文件
ECollisionChannel(Engine/EngineTypes.h:1098)包含 8 个引擎通道、6 个隐藏引擎槽位和 50 个游戏槽位(共 64 个;ECC_GameTraceChannel50 位于 :1168):
ECC_WorldStatic, ECC_WorldDynamic, ECC_Pawn, ECC_PhysicsBody,
ECC_Vehicle, ECC_Destructible // object channels (what a component IS)
ECC_Visibility, ECC_Camera // trace channels (what a query LOOKS FOR)
ECC_GameTraceChannel1 ... ECC_GameTraceChannel50 // project slots, either kind
- 对象通道 — 每个组件恰好有一个,通过
SetCollisionObjectType设置。由*ByObjectType查询匹配。 - 线迹通道 — 绝不会是对象类型;查询会将其传给
*ByChannel,然后由每个组件对此通道的响应决定阻挡/重叠/忽略。 ECollisionResponse(Engine/EngineTypes.h:1346):ECR_Ignore、ECR_Overlap、ECR_Block。ECollisionEnabled::Type(Engine/EngineTypes.h:1805):NoCollision、QueryOnly、PhysicsOnly、QueryAndPhysics、ProbeOnly、QueryAndProbe。
#include "Components/PrimitiveComponent.h"
MyMesh->SetCollisionProfileName(TEXT("BlockAllDynamic")); // preferred: one call sets everything
MyMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
MyMesh->SetCollisionObjectType(ECC_PhysicsBody);
MyMesh->SetCollisionResponseToAllChannels(ECR_Block);
MyMesh->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
MyMesh->SetCollisionResponseToChannel(ECC_Camera, ECR_Ignore);
FCollisionResponseContainer Responses(ECR_Ignore);
Responses.SetResponse(ECC_Visibility, ECR_Block);
MyMesh->SetCollisionResponseToChannels(Responses);
SetCollisionProfileName 会覆盖所有手动设置的响应,因此应先应用配置文件,再设置各通道的覆盖项。
Config/BaseEngine.ini 中 [/Script/Engine.CollisionProfile] 下提供的引擎配置文件:NoCollision、BlockAll、OverlapAll、BlockAllDynamic、OverlapAllDynamic、IgnoreOnlyPawn、OverlapOnlyPawn、Pawn、Spectator、CharacterMesh、PhysicsActor、Destructible、InvisibleWall、InvisibleWallDynamic、Trigger、Ragdoll、Vehicle、UI、WaterBodyCollision。
自定义通道和配置文件只能通过配置创建 — 没有运行时 API:
[/Script/Engine.CollisionProfile]
+DefaultChannelResponses=(Channel=ECC_GameTraceChannel1,DefaultResponse=ECR_Block,bTraceType=True,bStaticObject=False,Name="Weapon")
+DefaultChannelResponses=(Channel=ECC_GameTraceChannel2,DefaultResponse=ECR_Block,bTraceType=False,bStaticObject=False,Name="Interactable")
+Profiles=(Name="MyInteractable",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Interactable",CustomResponses=((Channel="Weapon",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Block)),HelpMessage="Interactable props")
+EditProfiles=(Name="Pawn",CustomResponses=((Channel="Weapon",Response=ECR_Block)))
bTraceType=True 表示线迹通道,False 表示对象通道。+Profiles= 添加或替换整个配置文件;+EditProfiles= 只修补现有配置文件中的响应(Engine/CollisionProfile.h:160-190)。UCollisionProfile::Get() 会读取该表;配置更改后,UCollisionProfile::LoadProfileConfig(bool bForceInit)(Engine/CollisionProfile.h:237)会重新读取配置。
完整配置文件集、ini 参考和调试检查清单,请参阅 collision-channel-setup.md。
轨迹扫掠和重叠查询
所有世界查询都是 UWorld 的 const 成员(Engine/World.h:2128-2441)。分为三类,每类都有 Test / Single / Multi 形式:
| 后缀 | 选择依据 | 额外参数 |
|---|---|---|
ByChannel | 查询方的轨迹通道与各组件的响应 | ECollisionChannel TraceChannel,可选的 FCollisionResponseParams |
ByObjectType | 目标组件的对象通道 | const FCollisionObjectQueryParams& |
ByProfile | 命名配置文件中存储的通道和响应 | FName ProfileName |
#include "Engine/World.h"
#include "Engine/HitResult.h"
#include "CollisionQueryParams.h"
#include "CollisionShape.h"
FCollisionQueryParams Params(TEXT("MyWeaponTrace"), /*bTraceComplex=*/false, GetOwner());
Params.bReturnPhysicalMaterial = true; // fills Hit.PhysMaterial
Params.bReturnFaceIndex = false; // fills Hit.FaceIndex; expensive
Params.bIgnoreTouches = true; // discard ECR_Overlap results (bIgnoreBlocks is the inverse)
Params.bFindInitialOverlaps = true; // report shapes already overlapping at Start
Params.MobilityType = EQueryMobilityType::Any; // Any | Static | Dynamic
Params.AddIgnoredActor(GetOwner());
Params.AddIgnoredComponent(MyMesh.Get()); // .Get(): a TObjectPtr member is ambiguous (CollisionQueryParams.h:267-269)
FHitResult Hit;
GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_Visibility, Params);
TArray<FHitResult> Hits;
GetWorld()->LineTraceMultiByChannel(Hits, Start, End, ECC_GameTraceChannel1, Params);
FCollisionObjectQueryParams ObjParams(ECC_Pawn);
ObjParams.AddObjectTypesToQuery(ECC_PhysicsBody);
GetWorld()->LineTraceSingleByObjectType(Hit, Start, End, ObjParams, Params);
GetWorld()->LineTraceSingleByProfile(Hit, Start, End, TEXT("BlockAll"), Params);
const bool bAnythingThere = GetWorld()->LineTraceTestByChannel(Start, End, ECC_Visibility, Params);
TraceTag 是第一个构造函数参数;它也用于控制控制台中的 TraceTagAll / 命名标签调试绘制。
扫掠查询需要旋转和一个 FCollisionShape(PhysicsCore/Public/CollisionShape.h:284-316):
const FCollisionShape Sphere = FCollisionShape::MakeSphere(30.f); // radius
const FCollisionShape Box = FCollisionShape::MakeBox(FVector(50.f, 30.f, 80.f));// half-extents
const FCollisionShape Capsule = FCollisionShape::MakeCapsule(34.f, 88.f); // radius, half-height
GetWorld()->SweepSingleByChannel(Hit, Start, End, FQuat::Identity, ECC_Pawn, Capsule, Params);
GetWorld()->SweepMultiByChannel(Hits, Start, End, FQuat::Identity, ECC_Pawn, Sphere, Params);
GetWorld()->SweepSingleByObjectType(Hit, Start, End, FQuat::Identity, ObjParams, Box, Params);
GetWorld()->SweepSingleByProfile(Hit, Start, End, FQuat::Identity, TEXT("Pawn"), Capsule, Params);
GetCapsuleHalfHeight() 包含端部半径;GetCapsuleAxisHalfLength() 仅表示轴杆长度的一半。
重叠查询是静止形状测试,返回 FOverlapResult(Engine/OverlapResult.h:12):
#include "Engine/OverlapResult.h"
TArray<FOverlapResult> Overlaps;
GetWorld()->OverlapMultiByObjectType(Overlaps, Center, FQuat::Identity,
FCollisionObjectQueryParams(ECC_Pawn), FCollisionShape::MakeSphere(500.f), Params);
GetWorld()->OverlapMultiByChannel(Overlaps, Center, FQuat::Identity, ECC_Pawn,
FCollisionShape::MakeSphere(500.f), Params);
const bool bBlocked = GetWorld()->OverlapAnyTestByChannel(Center, FQuat::Identity,
ECC_WorldStatic, FCollisionShape::MakeBox(FVector(40.f)), Params);
for (const FOverlapResult& Result : Overlaps)
{
AActor* Actor = Result.GetActor();
UPrimitiveComponent* Comp = Result.GetComponent();
}
OverlapAnyTestByChannel 在遇到第一个阻挡 或 接触时停止;OverlapBlockingTestByChannel 会忽略接触。如果要查询“自己的组件当前已与哪些对象接触”,请使用 UPrimitiveComponent::GetOverlappingActors 或 ComponentOverlapComponent,而不是世界查询。
异步轨迹查询会在本帧入队,并在下一帧读取(Engine/World.h:2522、WorldCollision.h:148):
#include "WorldCollision.h"
// EAsyncTraceType: Test | Single | Multi
FTraceHandle Handle = GetWorld()->AsyncLineTraceByChannel(
EAsyncTraceType::Single, Start, End, ECC_Visibility, Params);
FTraceDatum Datum;
if (GetWorld()->QueryTraceData(Handle, Datum) && Datum.OutHits.Num() > 0)
{
const FHitResult& First = Datum.OutHits[0];
}
传入 FTraceDelegate*(DECLARE_DELEGATE_TwoParams(FTraceDelegate, const FTraceHandle&, FTraceDatum&))时,则会在批次完成后调用回调。
请参阅 trace-patterns.md,了解命中扫描、近战扫掠、交互、地面检测、范围效果、视线检测和异步传感器模式,以及 Blueprint 层的 UKismetSystemLibrary 封装和性能指南。
FHitResult
Engine/HitResult.h。Actor 已不再是其成员;要获取 Actor,需通过访问器。
Hit.bBlockingHit; // :107 false for a touch-only result
Hit.bStartPenetrating; // :116 query began already overlapping; PenetrationDepth :91
Hit.Time; // :33 0..1 along Start->End; Distance :37 in cm
Hit.Location; // :45 where the swept shape's origin ended up
Hit.ImpactPoint; // :53 surface contact point
Hit.Normal; // :61 normal opposing movement at Location
Hit.ImpactNormal; // :69 geometric surface normal at ImpactPoint
Hit.TraceStart; // :76; TraceEnd :83
Hit.Item; // :99 instance index (ISM/HISM); ElementIndex :103 shape index
Hit.FaceIndex; // :26 needs Params.bReturnFaceIndex
Hit.BoneName; // :141 bone hit; MyBoneName :145 our own bone
Hit.PhysMaterial; // :123 TWeakObjectPtr, needs bReturnPhysicalMaterial
AActor* HitActor = Hit.GetActor(); // :211
UPrimitiveComponent* HitComp = Hit.GetComponent(); // :227
FActorInstanceHandle Handle = Hit.GetHitObjectHandle(); // :216, lightweight instances
对于 Multi 查询,只有最后一个元素可以是阻挡命中;此前的每个元素都是接触。
碰撞事件
委托是稀疏动态多播委托,因此处理函数必须在 头文件中 声明为 UFUNCTION(),且参数列表必须与委托完全一致,否则 AddDynamic 会在运行时绑定失败。签名来自 Components/PrimitiveComponent.h:274-282 和 GameFramework/Actor.h:194-196:
| 委托 | 参数 |
|---|---|
FComponentHitSignature | UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit |
FComponentBeginOverlapSignature | UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult |
FComponentEndOverlapSignature | UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex |
FComponentWakeSignature / FComponentSleepSignature | UPrimitiveComponent* WakingComponent(SleepingComponent)、FName BoneName |
FActorBeginOverlapSignature / FActorEndOverlapSignature | AActor* OverlappedActor, AActor* OtherActor |
FActorHitSignature | AActor* SelfActor, AActor* OtherActor, FVector NormalImpulse, const FHitResult& Hit |
// MyPickup.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Engine/HitResult.h"
#include "MyPickup.generated.h"
UCLASS()
class MYGAME_API AMyPickup : public AActor
{
GENERATED_BODY()
public:
AMyPickup();
protected:
virtual void BeginPlay() override;
UPROPERTY(VisibleAnywhere, Category = "Collision")
TObjectPtr<class USphereComponent> Trigger;
UPROPERTY(VisibleAnywhere, Category = "Collision")
TObjectPtr<class UStaticMeshComponent> Mesh;
UFUNCTION()
void OnBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION()
void OnEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
UFUNCTION()
void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
};
// MyPickup.cpp
#include "MyPickup.h"
#include "Components/SphereComponent.h"
#include "Components/StaticMeshComponent.h"
AMyPickup::AMyPickup()
{
Trigger = CreateDefaultSubobject<USphereComponent>(TEXT("Trigger"));
SetRootComponent(Trigger);
Trigger->SetSphereRadius(120.f);
Trigger->SetCollisionProfileName(TEXT("OverlapAllDynamic"));
Trigger->SetGenerateOverlapEvents(true);
Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
Mesh->SetupAttachment(Trigger);
Mesh->SetCollisionProfileName(TEXT("PhysicsActor"));
Mesh->SetSimulatePhysics(true);
Mesh->SetNotifyRigidBodyCollision(true); // "Simulation Generates Hit Events"
}
void AMyPickup::BeginPlay()
{
Super::BeginPlay();
Trigger->OnComponentBeginOverlap.AddDynamic(this, &AMyPickup::OnBeginOverlap);
Trigger->OnComponentEndOverlap.AddDynamic(this, &AMyPickup::OnEndOverlap);
Mesh->OnComponentHit.AddDynamic(this, &AMyPickup::OnHit);
}
void AMyPickup::OnBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
if (OtherActor && OtherActor != this)
{
Destroy();
}
}
void AMyPickup::OnEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
}
void AMyPickup::OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
const float ImpactStrength = NormalImpulse.Size();
}
要求:重叠事件需要至少一侧设置为 ECR_Overlap,双方组合不能为 ECR_Ignore,且 两个 组件都必须调用 SetGenerateOverlapEvents(true)(bGenerateOverlapEvents 是私有成员——Components/PrimitiveComponent.h:429)。模拟碰撞命中需要双方都设置为 ECR_Block,使用 QueryAndPhysics 或 PhysicsOnly,并在模拟组件上调用 SetNotifyRigidBodyCollision(true);扫掠移动(SetActorLocation(..., true)、移动组件)遇到任何阻挡命中时都会触发 OnComponentHit,无需设置该标志(PrimitiveComponent.cpp:3540-3550)。OnComponentWake / OnComponentSleep 需要在刚体上设置 bGenerateWakeEvents(PhysicsCore/Public/BodyInstanceCore.h:59)。
物理刚体与力
MyMesh->SetSimulatePhysics(true); // PrimitiveComponent.h:1661
MyMesh->SetEnableGravity(true); // :2801
MyMesh->SetMassOverrideInKg(NAME_None, 50.f, true); // :2857; GetMass() :2861
MyMesh->SetLinearDamping(0.1f); // :2825; SetAngularDamping :2833
MyMesh->SetUseCCD(true); // :2894 swept collision for fast bodies
MyMesh->SetConstraintMode(EDOFMode::XYPlane); // :1682 lock to a plane
const bool bSimulating = MyMesh->IsSimulatingPhysics(); // :2644
MyMesh->AddImpulse(FVector(0.f, 0.f, 1000.f), NAME_None, /*bVelChange=*/false); // :1692
MyMesh->AddImpulseAtLocation(FVector(500.f, 0.f, 0.f), Hit.ImpactPoint); // :1725 adds spin
MyMesh->AddRadialImpulse(Center, 500.f, 2000.f, RIF_Linear, /*bVelChange=*/false); // :1748
MyMesh->AddForce(FVector(0.f, 0.f, 9800.f), NAME_None, /*bAccelChange=*/false); // :1759 every tick
MyMesh->AddRadialForce(Center, 500.f, 3000.f, RIF_Constant, false); // :1793
MyMesh->SetPhysicsLinearVelocity(FVector(0.f, 0.f, 300.f)); // :1825 teleports velocity; use sparingly
const FVector Velocity = MyMesh->GetPhysicsLinearVelocity(); // :1832
MyMesh->WakeRigidBody(); // :1938; PutRigidBodyToSleep() :1945
bVelChange=true / bAccelChange=true 不受质量影响。ERadialImpulseFalloff 可以是 RIF_Constant 或 RIF_Linear(PhysicsCore/Public/Chaos/ChaosEngineInterface.h:90)。
MyMesh->GetBodyInstance() 可访问刚体。FBodyInstanceCore 标志(PhysicsCore/Public/BodyInstanceCore.h):bSimulatePhysics(:30)、bOverrideMass(:35)、bEnableGravity(:39)、bUpdateKinematicFromSimulation(:43)、bAutoWeld(:51)、bStartAwake(:55)、bGenerateWakeEvents(:59)。FBodyInstance 还添加了 bUseCCD(:418)、bNotifyRigidBodyCollision(:440)、LinearDamping(:629)、MassScale(:645)、InertiaTensorScale(:653)、SleepFamily(:410)和 CustomSleepThresholdMultiplier(:696)。ESleepFamily(Chaos/ChaosEngineInterface.h:101)包含 Normal、Sensitive 或 Custom;只有 Custom 会读取该乘数。
碰撞复杂度由 ECollisionTraceFlag(PhysicsCore/Public/BodySetupEnums.h:13-20)决定:CTF_UseDefault、CTF_UseSimpleAndComplex、CTF_UseSimpleAsComplex、CTF_UseComplexAsSimple。复杂碰撞体不能模拟物理。
约束、径向力与布娃娃
UPhysicsConstraintComponent(PhysicsEngine/PhysicsConstraintComponent.h:24)封装了一个 FConstraintInstance(:73)——Physics Asset 为每个关节存储的也是同一个结构体。
#include "PhysicsEngine/PhysicsConstraintComponent.h"
UPhysicsConstraintComponent* Joint = NewObject<UPhysicsConstraintComponent>(this);
Joint->SetupAttachment(RootComponent);
Joint->RegisterComponent();
Joint->SetConstrainedComponents(MeshA, NAME_None, MeshB, NAME_None); // :134
Joint->SetLinearXLimit(ELinearConstraintMotion::LCM_Locked, 0.f); // :270
Joint->SetAngularSwing1Limit(EAngularConstraintMotion::ACM_Limited, 45.f); // :291
Joint->SetAngularTwistLimit(EAngularConstraintMotion::ACM_Free, 0.f); // :305
Joint->SetAngularVelocityDriveTwistAndSwing(true, false); // :199
Joint->SetAngularDriveParams(500.f, 50.f, 0.f); // :262
Joint->SetDisableCollision(true); // :374
Joint->BreakConstraint(); // :142
运动枚举包括 ELinearConstraintMotion(LCM_Free、LCM_Limited、LCM_Locked)和 EAngularConstraintMotion(ACM_Free、ACM_Limited、ACM_Locked)。编辑器预设只是这些枚举的组合:Fixed 会锁定全部六个自由度,Hinge 会释放一个角轴,Prismatic 会释放一个线性轴,Ball-and-Socket 会释放所有角自由度。URadialForceComponent(PhysicsEngine/RadialForceComponent.h:16)是即插即用的爆炸/真空效果组件:包含 Radius、Falloff、ImpulseStrength、bImpulseVelChange、bIgnoreOwningActor、ForceStrength,还可调用 FireImpulse()(:50)施加一次性冲量,以及调用 AddObjectTypeToAffect(EObjectTypeQuery)(:54)筛选目标。
布娃娃通过骨骼网格体的 Physics Asset 运行(UPhysicsAsset::SkeletalBodySetups、ConstraintSetup——PhysicsEngine/PhysicsAsset.h:213-220):
GetMesh()->SetCollisionProfileName(TEXT("Ragdoll"));
GetMesh()->SetAllBodiesSimulatePhysics(true); // SkeletalMeshComponent.h:2344
GetMesh()->SetAllBodiesBelowSimulatePhysics(TEXT("spine_01"), true); // :2376 partial ragdoll
GetMesh()->SetPhysicsBlendWeight(1.f); // :2353 blend animation <-> sim
物理材质
PhysicsCore/Public/PhysicalMaterials/PhysicalMaterial.h:103。
float Friction; // :115 kinetic; StaticFriction :119
float Restitution; // :131 0 dead, 1 elastic
float Density; // :147 g/cm^3, drives mass from shape volume
float SleepLinearVelocityThreshold; // :151; SleepAngularVelocityThreshold :155
int32 SleepCounterThreshold; // :159; RaiseMassToPower :167
TEnumAsByte<EPhysicalSurface> SurfaceType; // :181
// FrictionCombineMode / RestitutionCombineMode are EFrictionCombineMode::Type and only
// apply when bOverrideFrictionCombineMode / bOverrideRestitutionCombineMode is set.
MyMesh->SetPhysMaterialOverride(MyPhysMaterial); // PrimitiveComponent.h:3008
if (UPhysicalMaterial* PhysMat = Hit.PhysMaterial.Get()) // needs bReturnPhysicalMaterial
{
const EPhysicalSurface Surface = UPhysicalMaterial::DetermineSurfaceType(PhysMat); // :242
}
EPhysicalSurface(PhysicsCore/Public/Chaos/ChaosEngineInterface.h:20)包含 SurfaceType_Default 和 SurfaceType1..SurfaceType62,可在 Project Settings > Physics 中命名。
Chaos 求解器和异步物理
Chaos 是 5.8 中唯一的物理后端。FChaosScene(PhysicsCore/Public/Chaos/ChaosScene.h:84)拥有求解器,并由 SetUpForFrame(:141)、StartFrame(:140)、EndFrame(:142)驱动;WaitPhysScenes(:122)会等待结果。
项目设置位于 [/Script/Engine.PhysicsSettings] 下的 UPhysicsSettings / UPhysicsSettingsCore:
| 设置 | 头文件 | 含义 |
|---|---|---|
DefaultGravityZ | PhysicsSettingsCore.h:25 | 世界重力,默认值为 -980 |
BounceThresholdVelocity | :79 | 低于此值时,不执行恢复系数计算 |
MaxDepenetrationVelocity | :95 | 限制推出速度;0 = 不限 |
bSimulateSkeletalMeshOnDedicatedServer | :114 | 关闭后可节省服务器上的布娃娃开销 |
DefaultShapeComplexity | :119 | 项目级 ECollisionTraceFlag |
SolverOptions | :123 | FChaosSolverConfiguration |
bSubstepping / MaxSubstepDeltaTime / MaxSubsteps | PhysicsSettings.h:325,341,345 | 修复小型高速刚体的穿透和抖动 |
bTickPhysicsAsync / AsyncFixedTimeStepSize | :333,337 | 固定频率的物理线程 |
PhysicsPrediction | :267 | FPhysicsPredictionSettings,用于控制是否启用重模拟 |
FChaosSolverConfiguration(Chaos/Public/ChaosSolverConfiguration.h:51)提供 PositionIterations、VelocityIterations、ProjectionIterations、CollisionMarginFraction、CollisionCullDistance、CollisionMaxPushOutVelocity。
5.8 中存在的诊断 CVar:
p.Chaos.DebugDraw.Enabled 1 ChaosDebugDrawComponent.cpp:29
p.Chaos.DebugDraw.Radius 3000 / p.Chaos.DebugDraw.MaxLines 20000 capture limits
p.Chaos.Solver.Iterations.Position|Velocity|Projection PBDRigidsSolver.cpp:337,340,343
p.Chaos.Solver.Deterministic :346 p.Chaos.Solver.UseCCD :406 (global CCD switch)
p.Chaos.DedicatedThreadEnabled ChaosSolversModule.cpp:23
启用 bTickPhysicsAsync 后,必须与求解器同步执行的游戏逻辑应使用异步 Tick,而不是 Tick:在 Actor 构造函数中设置 bAsyncPhysicsTickEnabled(受保护,GameFramework/Actor.h:699),或在组件上调用 SetAsyncPhysicsTickEnabled(true)(ActorComponent.h:1435;组件字段位于 Components/ActorComponent.h:407,为私有),然后重写 AsyncPhysicsTickActor(float DeltaTime, float SimTime)(Actor.h:2930)或 AsyncPhysicsTickComponent(float DeltaTime, float SimTime)(ActorComponent.h:985)。使用 UAsyncPhysicsInputComponent(Engine/Public/Physics/AsyncPhysicsInputComponent.h:10)将输入传递到异步线程。
在 FBodyInstance 之下,刚体通过 Chaos::FPhysicsObject 寻址。UPrimitiveComponent 实现了 IPhysicsComponent,并提供 GetPhysicsObjectById、GetPhysicsObjectByName 和 GetAllPhysicsObjects(Components/PrimitiveComponent.h:3248-3250);如果查询命中对象没有所属 Actor,FHitResult::PhysicsObject 和 PhysicsObjectOwner(Engine/HitResult.h:134-137)可标识该命中对象。
网络物理
物理模拟在服务器上运行。模拟运行的任何位置都会触发命中和重叠事件,因此除非 Actor 也在本地模拟或复制结果,否则客户端不会收到这些事件。
AActor::SetPhysicsReplicationMode(EPhysicsReplicationMode)/GetPhysicsReplicationMode()(GameFramework/Actor.h:924,928)可选择Default、PredictiveInterpolation、Resimulation或None(Engine/EngineTypes.h:3614)。Resimulation需要在项目设置 > 物理 > 物理预测中启用UPhysicsSettings::PhysicsPrediction;PredictiveInterpolation无需启用该设置即可运行,但启用后效果最佳(Engine/EngineTypes.h:3619-3629)。UNetworkPhysicsComponent(Engine/Public/Physics/NetworkPhysicsComponent.h:2290)会在物理线程上记录输入和状态历史,以便客户端从经过校正的过去状态重新模拟。- 对于不使用预测的 Actor,变换同步仍通过
bReplicateMovement/FRepMovement进行。
复制规则、条件和 RPC 设计属于 ue-networking-replication 的范围;此技能仅介绍应选择哪种物理模式。
模块依赖项
// MyGame.Build.cs
PublicDependencyModuleNames.AddRange(new string[] {
"Core", "CoreUObject",
"Engine", // UPrimitiveComponent, FHitResult, UWorld queries, UCollisionProfile
"PhysicsCore", // FCollisionShape, UPhysicalMaterial, FBodyInstanceCore, ECollisionTraceFlag
});
// Add "Chaos" only for Chaos:: types; "GeometryCollectionEngine"/"FieldSystemEngine" for destruction.
已弃用 — 请勿使用
| 请勿使用 | 在 5.8 中使用 | 来源 |
|---|---|---|
Hit.Actor | Hit.GetActor() / Hit.GetHitObjectHandle() | 没有 Actor 成员;HitObjectHandle 位于 Engine/HitResult.h:127 |
Comp->bGenerateOverlapEvents = true | Comp->SetGenerateOverlapEvents(true) | 私有字段,位于 Components/PrimitiveComponent.h:429 |
Params.GetIgnoredActors() | Params.GetIgnoredSourceObjects() | CollisionQueryParams.h:145 中标记为 UE_DEPRECATED(5.5) |
Params.ClearIgnoredActors() | Params.ClearIgnoredSourceObjects() | CollisionQueryParams.h:180 中标记为 UE_DEPRECATED(5.5) |
ObjParams.ObjectTypesToQuery | GetObjectTypesToQuery() / SetObjectTypesToQuery() | CollisionQueryParams.h:490 中标记为 UE_DEPRECATED(5.8) |
GetAllObjectsQueryFlag()、GetQueryBitfield() | 对应的 ...64() 形式 | CollisionQueryParams.h:368,571 中标记为 UE_DEPRECATED(5.8) |
Constraint->SetAngularVelocityDrive(bSwing, bTwist) | SetAngularVelocityDriveTwistAndSwing(bTwist, bSwing) — 参数顺序相反 | PhysicsEngine/PhysicsConstraintComponent.h:187-199 中的 DeprecatedFunction 元数据 |
PhysMat->DestructibleDamageThresholdScale | Geometry Collection 损伤阈值 | 已重命名为 _DEPRECATED,PhysicalMaterials/PhysicalMaterial.h:170 中标记为 UE_DEPRECATED(5.3) |
FChaosQueryFilterData / FQueryFilterData | ChaosInterface::FSceneQueryCommonParams | PhysicsInterfaceTypesCore.h:410 / ChaosInterfaceWrapperCore.h:35 中标记为 UE_DEPRECATED(5.8) |
ICollisionQueryFilterCallbackBase | ICollisionQueryFilterCallback | CollisionQueryFilterCallbackCore.h:63 中标记为 UE_DEPRECATED(5.8) |
GetQueryFilterData() / GetSimulationFilterData() | GetCombinedShapeFilterData() | ChaosInterfaceWrapperCore.h:98 中标记为 UE_DEPRECATED(5.7) |
BodySetup->ChaosTriMeshes | BodySetup->TriMeshGeometries(:278) | PhysicsEngine/BodySetup.h:308 中标记为 UE_DEPRECATED(5.4) |
PhysX 类型(PxRigidActor、PxScene、PhysX 模块) | Chaos 对应类型 | 5.8 不再提供 PhysX 模块 |
常见错误
在类外定义上使用 UFUNCTION(): UHT 只会读取头文件,因此在 .cpp 中 void AMyPickup::OnHit 上方写入 UFUNCTION() 不会生成反射数据,AddDynamic 会静默地绑定失败。请在类体内使用 UFUNCTION() 声明处理函数;在 .cpp 中直接定义。
处理函数签名不一致: AddDynamic 要求与委托完全匹配。缺少 int32 OtherBodyIndex 或按值传递 FHitResult 都会导致编译失败,而过时的旧签名副本则无法绑定。
只在一侧启用重叠事件: 两个组件都需要设置为非忽略响应,并调用 SetGenerateOverlapEvents(true)。只在触发器上设置,是最常见的“重叠事件怎么一直不触发”问题。
未调用 SetNotifyRigidBodyCollision(true) 就启用命中事件: 对于模拟刚体,仅设置 ECR_Block 并不足够;OnComponentHit 不会触发(扫掠移动仍会分发事件)。
先手动设置响应,再应用配置文件: SetCollisionProfileName 会重置整个响应容器。应先设置配置文件,再设置覆盖项。
混用通道类型: 对象类型为 ECC_Pawn 的组件,对查询 ECC_GameTraceChannel3 的 LineTraceSingleByObjectType 不可见;而跟踪通道永远不能作为对象类型。ini 中的 bTraceType 决定某个槽位属于哪种类型。
默认将 bTraceComplex = true: 逐三角形查询的成本是简单凸包的数倍,而且会跳过仅支持简单碰撞的刚体。只有在需要三角形精度时才启用。
将 SetPhysicsLinearVelocity 用作移动 API: 每次调用都会覆盖求解器的速度并干扰接触。除非你有意直接设置速度,否则请使用 AddForce / AddImpulse。
高速刚体发生穿透: 在刚体上启用 SetUseCCD(true),或在整个项目中启用子步进,而不要全局缩小时间步长。对于每帧对许多 Actor 执行的跟踪,请使用 AsyncLineTraceByChannel,或使用计时器将频率限制为 5–10 Hz。
相关技能
ue-actor-component-architecture— 形状和网格组件的创建、附加、注册与生命周期ue-character-movement—UCharacterMovementComponent的地面检测、胶囊体扫掠和跨越台阶逻辑ue-networking-replication— 对任何与物理结果相关的内容使用DOREPLIFETIME、复制条件和 RPCue-ai-navigation— 感知追踪、导航网格查询和UNavigationSystemV1ue-procedural-generation— 运行时生成网格的碰撞设置- 另请参阅:
ue-testing-debugging、ue-gameplay-framework、ue-mover