Collision
A Realtime Mesh can drive both simple collision (analytic shapes — spheres, boxes, capsules, convex hulls) and complex collision (per-triangle geometry). The mesh owns the collision data and cooks it into a UBodySetup; the component reports collision to the engine like any other primitive. This page covers the configuration, the two geometry paths, custom collision meshes, and async cooking.
Everything here is grounded in ARealtimeMeshExample_Simple_Collision (Source/RealtimeMeshExamples/Private/Simple/RealtimeMeshExample_Simple_Collision.cpp) and the collision types in Public/Interface/Core/RealtimeMeshCollision.h. The collision API lives on URealtimeMeshManaged, so it’s available on Simple, Procedural, and Dynamic meshes.
Collision configuration
Section titled “Collision configuration”FRealtimeMeshCollisionConfiguration is the top-level switch. The full set of flags (with defaults):
struct FRealtimeMeshCollisionConfiguration{ bool bUseComplexAsSimpleCollision; // default true bool bUseAsyncCook; // default true bool bShouldFastCookMeshes; // default false bool bFlipNormals; // default false bool bDeformableMesh; // default false bool bMergeAllMeshes; // default false};bUseComplexAsSimpleCollision— when true, the rendered/complex triangles are also used to satisfy simple collision queries. Turn it off when you supply analytic simple shapes and don’t want per-triangle collision.bUseAsyncCook— cook collision on a worker thread instead of blocking the game thread. On by default; recommended.bShouldFastCookMeshes— use the faster (lower-quality) cook path.bFlipNormals— flip collision-mesh winding.bDeformableMesh— hint for meshes whose collision updates frequently.bMergeAllMeshes— merge all collision meshes into one during cook.
Apply it with SetCollisionConfig:
FRealtimeMeshCollisionConfiguration CollisionConfig;CollisionConfig.bUseComplexAsSimpleCollision = false; // simple shapes onlyCollisionConfig.bUseAsyncCook = true;RealtimeMesh->SetCollisionConfig(CollisionConfig);
FRealtimeMeshCollisionConfiguration Current = RealtimeMesh->GetCollisionConfig();SetCollisionConfig returns a TFuture<ERealtimeMeshCollisionUpdateResult> (and has a Blueprint overload taking a completion callback). See Update results below.
Simple geometry — analytic shapes
Section titled “Simple geometry — analytic shapes”FRealtimeMeshSimpleGeometry is a bundle of shape sets, one per shape kind:
struct FRealtimeMeshSimpleGeometry{ FSimpleShapeSet<FRealtimeMeshCollisionSphere> Spheres; FSimpleShapeSet<FRealtimeMeshCollisionBox> Boxes; FSimpleShapeSet<FRealtimeMeshCollisionCapsule> Capsules; FSimpleShapeSet<FRealtimeMeshCollisionTaperedCapsule> TaperedCapsules; FSimpleShapeSet<FRealtimeMeshCollisionConvex> ConvexHulls;};Every shape shares a base (FRealtimeMeshCollisionShape) with a Name, Center, Rotation, and bContributesToMass. The per-shape fields:
- Sphere —
Radius. - Box —
Extents. These are full dimensions (they map straight toFKBoxElem.X/Y/Z), so a box built from a half-extent of 250 needsExtentsof 500. - Capsule —
Radius,Length. - Tapered capsule —
RadiusA,RadiusB,Length. - Convex hull — a set of vertices via
SetVertices(...).
Build the geometry, add shapes to the relevant set, and apply it with SetSimpleGeometry (returns a TFuture<ERealtimeMeshCollisionUpdateResult>; Blueprint overload takes a callback):
// A 500x500x10 analytic box, matching a rendered 250x250x5 half-extent floor.FRealtimeMeshSimpleGeometry SimpleGeometry;FRealtimeMeshCollisionBox CollisionBox;CollisionBox.Extents = FVector(500.0f, 500.0f, 10.0f);CollisionBox.Center = FVector::ZeroVector;CollisionBox.Rotation = FRotator::ZeroRotator;CollisionBox.bContributesToMass = true;SimpleGeometry.Boxes.Add(CollisionBox);RealtimeMesh->SetSimpleGeometry(SimpleGeometry);
FRealtimeMeshSimpleGeometry Current = RealtimeMesh->GetSimpleGeometry();Analytic shapes are far cheaper than per-triangle collision — prefer them (with bUseComplexAsSimpleCollision = false) whenever a few primitives approximate your mesh well enough. This is the BuildSimpleCollision path in the example.
Each FSimpleShapeSet supports Add, Insert, Update, Remove (by index or name), GetByIndex, GetByName, and GetIndexFromName if you want to manage shapes by name.
Complex collision from rendered sections
Section titled “Complex collision from rendered sections”Instead of authoring separate collision geometry, you can promote the mesh’s own rendered triangles to complex collision. Two things drive it:
bUseComplexAsSimpleCollision = truein the config, so complex geometry also answers simple queries.- Per-section opt-in via
bShouldCreateCollision— a section only contributes its triangles to collision when it’s flagged.
You flag a section at creation or via its config update:
// When creating a section explicitly:RealtimeMesh->CreateSection(SectionKey, FRealtimeMeshSectionConfig(0), StreamRange, /*bShouldCreateCollision=*/ true);
// Or when updating an (e.g. auto-created) section's config:RealtimeMesh->UpdateSectionConfig(SectionKey, FRealtimeMeshSectionConfig(0), /*bShouldCreateCollision=*/ true);The example’s BuildComplexCollision sets bUseComplexAsSimpleCollision = true and builds a stepped platform whose rendered triangles become the collision surface — no separate collision geometry is authored.
Complex collision is per-triangle and comparatively expensive to cook and query. Reserve it for shapes that genuinely need it (arbitrary terrain, static level geometry) and lean on simple shapes for everything else.
Custom complex mesh geometry
Section titled “Custom complex mesh geometry”When you want per-triangle collision that is different from the rendered mesh (a lower-poly collision proxy, say), supply it directly as FRealtimeMeshComplexGeometry — a collection of FRealtimeMeshCollisionMesh objects:
FRealtimeMeshCollisionMesh CollisionMesh;CollisionMesh.Name = FName("Proxy");CollisionMesh.SetVertices(MoveTemp(Vertices)); // TArray<FVector3f>CollisionMesh.SetTriangles(MoveTemp(Triangles)); // TArray<TIndex3<int32>>// optional: SetMaterials(...), SetTexCoords(...)
FRealtimeMeshComplexGeometry ComplexGeometry;ComplexGeometry.Add(MoveTemp(CollisionMesh));RealtimeMesh->SetCustomComplexMeshGeometry(MoveTemp(ComplexGeometry));The full API on the mesh (all returning TFuture<ERealtimeMeshCollisionUpdateResult>):
SetCustomComplexMeshGeometry(...)— by rvalue or const-ref.EditCustomComplexMeshGeometry(TFunctionRef<void(FRealtimeMeshComplexGeometry&)>)— mutate the current geometry in place.ClearCustomComplexMeshGeometry()— remove it.ProcessCustomComplexMeshGeometry(TFunctionRef<void(const FRealtimeMeshComplexGeometry&)>)— read it under lock (don’t retain the reference past the callback).HasCustomComplexMeshGeometry()— whether any custom meshes are set.
RealtimeMesh->EditCustomComplexMeshGeometry([](FRealtimeMeshComplexGeometry& Geometry){ Geometry.GetByIndex(0).SetVertices(NewVertices);});FRealtimeMeshCollisionMesh caches its cooked result and re-cooks lazily whenever you change its vertices/triangles/materials/UVs, so repeated edits only pay for what changed.
Async cooking and update results
Section titled “Async cooking and update results”With bUseAsyncCook on (the default), collision cooks on a worker thread and applies when it finishes — so the game thread never blocks on a cook. Because a newer update can supersede an in-flight cook, each collision call reports how it resolved via ERealtimeMeshCollisionUpdateResult:
Updated— the collision was updated and applied.Ignored— the update was superseded by a newer one and dropped.Error— the update failed.Unknown— default/unset.
Await the future (or use the Blueprint completion callback) if you need to react:
RealtimeMesh->SetSimpleGeometry(SimpleGeometry) .Next([](ERealtimeMeshCollisionUpdateResult Result) { /* ... */ });Component-level collision behavior
Section titled “Component-level collision behavior”Two flags on URealtimeMeshComponent (see Public/RealtimeMeshComponent.h) tune what happens when collision updates:
KeepMomentumOnCollisionUpdate(defaultfalse) — preserve the body’s velocity across a collision rebuild instead of resetting it.bUpdateNavigationOnCollisionUpdate(defaulttrue) — refresh the navigation system on each collision update. Disable this for frequently-updated deformable collision meshes to avoid continuous navmesh tile rebuilds. (The navigation update is also skipped automatically when the component can’t affect navigation, regardless of this flag.)
Helper libraries
Section titled “Helper libraries”Two Blueprint function libraries assist with collision (Public/RealtimeMeshCollisionLibrary.h):
URealtimeMeshCollisionTools— utilities:FindCollisionUVRealtimeMesh(UV at a hit),CookConvexHull/CookComplexMesh(pre-cook a shape so it isn’t cooked lazily later), andAppendStreamsToCollisionMesh(fill anFRealtimeMeshCollisionMeshfrom aStreamSet, optionally a triangle sub-range).URealtimeMeshSimpleGeometryFunctionLibrary— Blueprint-friendlyAdd/Insert/Get/Update/Remove(by index and by name) for each simple shape kind (spheres, boxes, capsules, tapered capsules, convex hulls) operating on anFRealtimeMeshSimpleGeometry.
From C++ you can also look up a UV coordinate at a hit directly on the mesh with URealtimeMesh::CalcTexCoordAtLocation(...).