Quick answer: Pass bCreateCollision = true in CreateMeshSection and set bUseComplexAsSimpleCollision = true on the ProceduralMeshComponent. Without both, the mesh renders but has no physics collision. For dynamic objects, use AddCollisionConvexMesh instead of complex collision.
Here is how to fix Unreal procedural mesh missing collision. You generate terrain or a destructible wall at runtime using ProceduralMeshComponent. The mesh renders perfectly — you can see it, lighting works, materials apply. But the player walks straight through it. Line traces pass through it. Physics objects fall through it. The mesh has zero collision presence despite being visually solid.
The Symptom
A ProceduralMeshComponent renders correctly but has no collision. Pawns walk through it, projectile traces ignore it, and physics simulations treat it as non-existent. The component’s collision settings in the Details panel show collision enabled, but at runtime nothing collides with the generated geometry.
What Causes This
bCreateCollision not set in CreateMeshSection. The CreateMeshSection function has a boolean parameter bCreateCollision that defaults to false. If you omit it or pass false, no collision geometry is generated for that section.
bUseComplexAsSimpleCollision not enabled. Even with bCreateCollision = true, the physics engine needs to know what shape to use. Without bUseComplexAsSimpleCollision, it looks for simple collision (convex hulls). If none exist, there is no collision.
Collision cooking failed silently. If the mesh has degenerate triangles (zero-area faces, NaN vertices), the physics cooking step fails without visible errors. The mesh renders fine but the collision body is empty.
Component collision settings overridden. The component’s collision enabled setting or collision profile may be set to NoCollision or QueryOnly without physics. Check both the component defaults and any runtime modifications.
The Fix
Step 1: Enable collision in CreateMeshSection.
// Create mesh section WITH collision
ProceduralMesh->CreateMeshSection_Linear(
0, // Section index
Vertices, // TArray<FVector>
Triangles, // TArray<int32>
Normals, // TArray<FVector>
UVs, // TArray<FVector2D>
VertexColors, // TArray<FLinearColor>
Tangents, // TArray<FProcMeshTangent>
true // bCreateCollision = TRUE
);
The last parameter is the key. Without it, you get a visual-only mesh.
Step 2: Set bUseComplexAsSimpleCollision.
// In constructor or BeginPlay:
ProceduralMesh->bUseComplexAsSimpleCollision = true;
// Also ensure collision is enabled on the component:
ProceduralMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
ProceduralMesh->SetCollisionProfileName("BlockAll");
This tells the physics engine to use the mesh triangles directly as collision geometry. Without this flag, it expects separate convex hulls which procedural meshes do not have by default.
Step 3: Validate mesh data before creation. Degenerate geometry causes silent cooking failures:
// Validate triangles before creating the mesh
bool ValidateMeshData(const TArray<FVector>& Verts,
const TArray<int32>& Tris)
{
for (int32 i = 0; i < Tris.Num(); i += 3)
{
FVector A = Verts[Tris[i]];
FVector B = Verts[Tris[i+1]];
FVector C = Verts[Tris[i+2]];
FVector Cross = FVector::CrossProduct(B - A, C - A);
if (Cross.SizeSquared() < KINDA_SMALL_NUMBER)
{
UE_LOG(LogTemp, Warning, TEXT("Degenerate tri at %d"), i/3);
return false;
}
}
return true;
}
Step 4: Use convex collision for dynamic objects. Complex collision (triangle mesh) only works for static objects. For dynamic procedural meshes, add convex hulls:
// Add convex collision for a dynamic procedural mesh
TArray<FVector> ConvexVerts;
// ... compute convex hull vertices ...
ProceduralMesh->AddCollisionConvexMesh(ConvexVerts);
// For multiple convex sections (decomposition):
for (const auto& Hull : ConvexHulls)
{
ProceduralMesh->AddCollisionConvexMesh(Hull.Vertices);
}
“Rendering and collision are separate systems. A visible mesh is not automatically physical. Explicitly create collision or the physics world does not know it exists.”
Related Issues
For collision channel configuration on procedural meshes, see Collision Channel Not Blocking. For level streaming and dynamic content, see Level Instance Not Loading.
bCreateCollision true, bUseComplexAsSimple true, valid geometry. Procedural mesh collides.