Skip to content
CRUSHFORMV1.1.0
GET IT ON FAB

SURFACESWHEELS AND SURFACES

Wheels and surfaces

The ground decides what a wheel does: how much dirt it throws onto the body, and what the tyre sounds and looks like on it. The plugin finds the ground under each wheel from its physical material, then looks that material up in a table. This page covers the table, the wheels the body reads, and how to add a ground of your own.

Crushform
Surfaces7 Map elements
Index [ 0 ]
KeyPM_CF_Asphalt
Value
Dirt
Rolling SoundSW_CF_Tire_Asphalt_Loop
Rolling FXNS_CF_Rolling_Asphalt
Rolling FX Intensity Parameter
Index [ 1 ]
Index [ 2 ]
Index [ 3 ]
Index [ 4 ]
Index [ 5 ]
Index [ 6 ]
Default
The Surfaces table of DA_CF_Surfaces: one row per physical material, with its first row open to the rolling sound and effect.

Each physical material you use for a ground has a row in the surfaces table. The row says what a wheel on that ground gives the body in dirt, which rolling sound it plays and which rolling effect it shows. The shipped asset, DA_CF_Surfaces, has rows for the seven physical materials the plugin ships.

The Proving Ground's five ground strips side by side on an orange floor, labelled from left to right WATER, MUD, SNOW, DIRT and ASPHALT.
The five ground strips of the Proving Ground, from left to right: WATER, MUD, SNOW, DIRT and ASPHALT.
  • The table. UCFSurfaces is a data asset. Its Surfaces map holds one FCFSurface row per physical material, and its Default row serves a ground with no row, and a wheel in the air, which has no material. An empty field in a row stays empty. It does not borrow the Default row’s sound or effect.
  • A row. FCFSurface has four fields. Dirt holds the rates of dust, mud, snow and wetness the ground gives the body, and the rates that take each kind off it. Rolling Sound is the sound a tyre makes on it. Rolling FX is a Niagara system. Rolling FX Intensity Parameter names the float parameter that takes the intensity. When it is empty, the project’s FXIntensityParameter is used.
  • The wheels. The wheels come from the vehicle. The generic vehicle adapter (UCFVehicleAdapterComponent) traces the wheels in its Wheels list. Each entry takes a Wheel component on the owner: the wheel’s mesh, or the scene component the wheel turns on, which is how a wheel that sits on a bone is named. Radius is the tyre radius in cm, and 0 takes half the height of the mesh’s bounds the first time the list is used. Ground Channel is the channel the wheels trace, and Ground Probe Margin lets a tyre that is a few centimetres above the ground still touch it. A vehicle-system adapter, such as the Chaos one, reads its own wheels and ignores this list.
  • Wheel contacts. Each frame the owner’s host reports one FCFWheelContact per wheel: where the tyre touches the ground, its radius, its surface speed, its physical material, whether it is on the ground, and a WheelIndex that stays the same for the wheel’s life. A wheel in the air has its point and material left unread. The vehicle adapter fills these contacts from Build Wheel Contacts. Any other actor answers ICFWheelHost itself. With no host, the body has no wheels and never gets dirty.
  • Reading a wheel. The deform component keeps what each wheel stands on. GetWheelSurface returns the current state of a wheel, and OnWheelSurfaceChanged fires when a wheel’s physical material or grounded state changes. A change of speed alone fires nothing.
  • Dedicated server. The wheel-surface check does not run on a dedicated server.
  1. In the Content Browser, choose Add > Miscellaneous > Data Asset, pick the class CFSurfaces and name the asset DA_MyGame_Surfaces.
  2. Open it. Set Default to the row you want for any ground without its own row.
  3. Add an entry to Surfaces for each ground. Pick the ground’s physical material as the key, then fill its Dirt rates, Rolling Sound and Rolling FX. Leave a field empty for none.
  4. In Project Settings > Plugins > Crushform, set Surfaces to DA_MyGame_Surfaces.
  5. On the vehicle’s adapter, fill Wheels with one entry per wheel: pick its Wheel component and set its Radius, or leave the radius at 0 to take it from the mesh.
  6. Set the physical material on each ground’s material under Physical Material, as Phys Material. The Core concepts page describes the surfaces setup in more detail.

Blueprint can read a wheel’s surface from the deform component. The graph below runs when the custom event ReadWheelZero fires, reads wheel 0 and prints whether the wheel is on the ground.

The graph runs when the custom event ReadWheelZero fires and prints whether wheel 0 is on the ground.

From C++, read the surface of a wheel and check that it is on the ground before you use the material. The call needs the Crushform module (CFDeformComponent.h), which you add to PublicDependencyModuleNames in your module’s Build.cs.

const FCFWheelSurface Surface = Deform->GetWheelSurface(0);
if (Surface.bGrounded && IsValid(Surface.PhysicalMaterial))
{
// The wheel stands on Surface.PhysicalMaterial.
}

A custom vehicle that does not use the adapter implements the wheel host and reports its wheels. The interface method is in CFWheelHost.h of the Crushform module, and it has this signature:

void GatherCrushformWheelContacts_Implementation(TArray<FCFWheelContact>& OutContacts) const override;

Give each wheel the same WheelIndex every frame, and report every wheel, grounded or not, so that a wheel leaving the ground is seen.

PropertyWhat it doesDefault
FCFSurface::DirtThe dirt rates of the surface, per second at the reference speed: Dust, Mud, Snow, Wet, and what each takes off the body.0 for every rate
FCFSurface::RollingSoundThe rolling sound on the surface. None means no rolling sound there.None
FCFSurface::RollingFXThe Niagara rolling effect on the surface. None means no effect there.None
FCFSurface::RollingFXIntensityParameterThe float parameter of the effect that takes the intensity.None, which uses UCFWheelSettings::FXIntensityParameter
UCFSurfaces::SurfacesOne row per physical material.Empty in the class; the shipped asset has rows for seven
UCFSurfaces::DefaultThe row of a ground with no row, and of a wheel in the air.A row with empty fields
UCFSettings::SurfacesThe surfaces asset the project uses./Crushform/Surfaces/DA_CF_Surfaces
FCFAdapterWheel::RadiusThe tyre radius of a wheel in the adapter’s list, in cm. 0 takes half the height of the mesh’s bounds.0
UCFVehicleAdapterComponent::GroundChannelThe channel the adapter’s wheels trace the ground on.ECC_Visibility
FCFWheelContact::bGroundedWhether the wheel touches the ground. A wheel in the air leaves no dirt.true
FCFWheelContact::WheelIndexThe wheel’s index, stable for its life. INDEX_NONE means the host does not name its wheels.INDEX_NONE

Open DA_MyGame_Surfaces and check that it lists your grounds and the Default row. Then play Lvl_ProvingGround and drive the sedan from the ASPHALT strip onto the DIRT strip. The rolling sound crossfades to the new surface once it has held under the wheel for the tuning’s Surface Hold Time, 0.15 seconds by default.

The wheels stay silent and the log says the surfaces do not load

Section titled “The wheels stay silent and the log says the surfaces do not load”

Cause. The soft reference in the project settings points to an asset that does not load. The plugin logs once and the wheels stay silent:

Crushform surfaces %s do not load; wheels stay silent

Fix. Set Surfaces in Project Settings > Plugins > Crushform to an asset that exists, such as /Crushform/Surfaces/DA_CF_Surfaces.

Cause. An empty Rolling Sound means no sound on that ground. The row does not borrow the Default row’s sound.

Fix. Set Rolling Sound on that row. The sound should be a looping asset: a sound that is not looping is logged, and it cuts out between restarts.

Cause. The ground’s material has no physical material, or its physical material has no row. Either way the wheel gets the Default row.

Fix. Set Phys Material on the ground’s material, and add a row for that physical material to the surfaces table.

Cause. A host that gives a wheel INDEX_NONE as its WheelIndex never gets that wheel reported to OnWheelSurfaceChanged. A host that reports only the wheels on the ground never sees a wheel leave it, because a wheel is grounded unless the host says otherwise.

Fix. Give each wheel a WheelIndex from 0 up, and report every wheel each frame, with bGrounded set to false while it is in the air.