mcpField
U
ue-physics-collision

ue-physics-collision

github.com/quodsoler开发4191.2k 次安装4 周前更新
代码仓库

关于 ue-physics-collision

面向 Unreal Engine 5.8 开发者的物理与碰撞技能,涵盖查询、碰撞事件、刚体和 Chaos 设置,并提供与当前头文件对应的 API 参考。

主要功能

  1. 01碰撞通道与配置文件
  2. 02射线、扫掠和重叠查询
  3. 03FHitResult 命中信息
  4. 04碰撞与唤醒事件
  5. 05刚体、力和物理材质
  6. 06Chaos 求解器与网络物理

适用场景

  1. 01配置对象与追踪通道
  2. 02实现命中、交互或范围检测
  3. 03响应组件重叠与碰撞
  4. 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:

委托参数
FComponentHitSignatureUPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit
FComponentBeginOverlapSignatureUPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult
FComponentEndOverlapSignatureUPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex
FComponentWakeSignature / FComponentSleepSignatureUPrimitiveComponent* WakingComponent(SleepingComponent)、FName BoneName
FActorBeginOverlapSignature / FActorEndOverlapSignatureAActor* OverlappedActor, AActor* OtherActor
FActorHitSignatureAActor* 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:

设置头文件含义
DefaultGravityZPhysicsSettingsCore.h:25世界重力,默认值为 -980
BounceThresholdVelocity:79低于此值时,不执行恢复系数计算
MaxDepenetrationVelocity:95限制推出速度;0 = 不限
bSimulateSkeletalMeshOnDedicatedServer:114关闭后可节省服务器上的布娃娃开销
DefaultShapeComplexity:119项目级 ECollisionTraceFlag
SolverOptions:123FChaosSolverConfiguration
bSubstepping / MaxSubstepDeltaTime / MaxSubstepsPhysicsSettings.h:325,341,345修复小型高速刚体的穿透和抖动
bTickPhysicsAsync / AsyncFixedTimeStepSize:333,337固定频率的物理线程
PhysicsPrediction:267FPhysicsPredictionSettings,用于控制是否启用重模拟

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.ActorHit.GetActor() / Hit.GetHitObjectHandle()没有 Actor 成员;HitObjectHandle 位于 Engine/HitResult.h:127
Comp->bGenerateOverlapEvents = trueComp->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.ObjectTypesToQueryGetObjectTypesToQuery() / 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->DestructibleDamageThresholdScaleGeometry Collection 损伤阈值已重命名为 _DEPRECATED,PhysicalMaterials/PhysicalMaterial.h:170 中标记为 UE_DEPRECATED(5.3)
FChaosQueryFilterData / FQueryFilterDataChaosInterface::FSceneQueryCommonParamsPhysicsInterfaceTypesCore.h:410 / ChaosInterfaceWrapperCore.h:35 中标记为 UE_DEPRECATED(5.8)
ICollisionQueryFilterCallbackBaseICollisionQueryFilterCallbackCollisionQueryFilterCallbackCore.h:63 中标记为 UE_DEPRECATED(5.8)
GetQueryFilterData() / GetSimulationFilterData()GetCombinedShapeFilterData()ChaosInterfaceWrapperCore.h:98 中标记为 UE_DEPRECATED(5.7)
BodySetup->ChaosTriMeshesBodySetup->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、复制条件和 RPC
  • ue-ai-navigation — 感知追踪、导航网格查询和 UNavigationSystemV1
  • ue-procedural-generation — 运行时生成网格的碰撞设置
  • 另请参阅:ue-testing-debugging、ue-gameplay-framework、ue-mover

评论

支持 Markdown