Skip to content
CRUSHFORMV1.1.0
GET IT ON FAB

INTEGRATIONHOST INTERFACES

Host interfaces

The vehicle adapter answers these questions for you on a vehicle. Use the interfaces when you have no adapter, or when your game has a host of its own. Your own vehicle walks through the adapter and a complete wheel host, and Core concepts has the summary table. This page covers each interface in turn: what it is asked, what it answers by default, and how to implement it.

A component asks its owner through the host it finds, in this order:

  1. The explicit host, set with SetCrushformHost before BeginPlay. C++ only.
  2. The object registered for the owner in UCFHostRegistry, a world subsystem. Its Register, Unregister and FindHost are C++ only.
  3. The owner itself, when its class implements the interface.
  4. No host. The component then answers as a body with no host.

Your own vehicle gives the same order in one paragraph.

The vehicle adapter registers itself as the host of the five interfaces it implements: ICFDeformHost, ICFWheelHost, ICFBreakerHost, ICFTireHost and ICFEngineHost. A vehicle with an adapter needs no host of its own for them. ICFTireViewer is not implemented by the adapter. It belongs to the pawn that the player sits in.

A host that does not implement the interface is refused with an error, which is described in the Troubleshooting section at the end of this page. A second host for the same actor and interface replaces the first, with a warning.

Registering a host for an actor in the registry lets every component of that actor find it, without a SetCrushformHost call. The registry is a world subsystem, so reach it through the world. In the actor’s BeginPlay, register the actor itself as its wheel host, and remove the registration in EndPlay:

#include "CFHostRegistry.h"
#include "CFWheelHost.h"
#include "Engine/World.h"
void AMyVehicle::BeginPlay()
{
Super::BeginPlay();
if (UCFHostRegistry* Registry = GetWorld()->GetSubsystem<UCFHostRegistry>())
{
// This actor implements ICFWheelHost.
Registry->Register(*this, *UCFWheelHost::StaticClass(), *this);
}
}
void AMyVehicle::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
if (UCFHostRegistry* Registry = GetWorld()->GetSubsystem<UCFHostRegistry>())
{
Registry->Unregister(*this, *UCFWheelHost::StaticClass(), *this);
}
Super::EndPlay(EndPlayReason);
}
  • Unregister removes the entry only while it still holds that host.
  • Registering the same host again changes nothing and logs nothing.
  • FindHost also works without the registry. It then answers with the actor itself, when the actor implements the interface.

Every answer is an ECFHostAnswer:

ValueMeaning
DefaultDecide as the component does with no host.
YesYes.
NoNo.

An event you leave untouched in Blueprint returns the same answer as no host. An ECFHostAnswer event returns Default. An event that returns a list gathers nothing. GatherCrushformIgnoredActors returns false, which keeps the owner’s attached actors ignored.

Do not call an interface event by name on an interface pointer. Your own vehicle shows the Execute_ call to use instead.

The lattice of a UCFDeformComponent asks it, and so does the UCFNetImpactFeedbackComponent of a vehicle body. Implement it on the owner.

EventAnswersDefault
IsCrushformImpactSourceHereECFHostAnswer: does this machine simulate the body and see its contactsThe authority
GatherCrushformIgnoredActorsOutput array of actors that never deform the owner, and a bool that replaces the owner’s attached actorsFalse, and nothing gathered
ShouldCrushformIgnoreBool for one actor, asked before the list above: a person walking into a car, sayFalse, which ignores none
IsCrushformReportPlausibleECFHostAnswer for a client’s impact report. The server asks it after its own checksPlausible
ShouldCrushformDeformECFHostAnswer for one mesh. The profile’s tags have already let the mesh throughDeforms
GatherCrushformWheelMeshesOutput array of the wheel meshes. They take dirt and never deformNothing
GetCrushformGlassThe owner’s UCFGlassUnitsComponent, or nullNull

Blueprint. Add the interface in Class Settings > Implemented Interfaces > Add. In My Blueprint, its functions are listed under Interfaces > Crushform, by their C++ names without spaces, such as IsCrushformImpactSourceHere. The graph below implements that function. It prints when the function runs, and it leaves the answer at Default.

The Implemented Interfaces list in Class Settings, with the Add button in the header row and CFDeform Host and CFWheel Host listed below it.
Class Settings after the two interfaces are added.
The graph implements IsCrushformImpactSourceHere and leaves the answer at Default.

C++. Implement the interface on the actor and override the _Implementation of each event you answer.

#include "CFDeformHost.h"
UCLASS()
class AMyVehicle : public AActor, public ICFDeformHost
{
GENERATED_BODY()
public:
virtual ECFHostAnswer IsCrushformImpactSourceHere_Implementation() const override;
virtual bool ShouldCrushformIgnore_Implementation(const AActor* Actor) const override;
virtual ECFHostAnswer ShouldCrushformDeform_Implementation(const UMeshComponent* Mesh) const override;
virtual UCFGlassUnitsComponent* GetCrushformGlass_Implementation() const override;
};
ECFHostAnswer AMyVehicle::IsCrushformImpactSourceHere_Implementation() const
{
// Only the machine that simulates the body judges its contacts.
return HasAuthority() ? ECFHostAnswer::Yes : ECFHostAnswer::No;
}

The dirt layer of the body, UCFRollingAudioComponent and UCFRollingFXComponent ask it. A Blueprint host is asked once a frame, and every caller of that frame shares its answer. The event fills one FCFWheelContact for each wheel, on the ground or not:

  • WheelIndex must stay the same for a wheel from frame to frame.
  • A wheel in the air keeps bGrounded false. Its Point and PhysicalMaterial are not read.
  • A host that reports only the wheels on the ground is correct, but then no wheel is ever seen to leave the ground.

With no host the list is empty, and the body never gets dirty.

Blueprint. Add the interface in Class Settings > Implemented Interfaces > Add, then implement GatherCrushformWheelContacts in My Blueprint. It has an output array, so it is a function. The graph below implements it, prints when it runs, and leaves the array empty. Fill the array in your own graph.

The graph implements GatherCrushformWheelContacts and leaves the array empty.

C++. Declare the override in the class, as in the deform block above, and define it. Your own vehicle builds a complete wheel host, with a trace from each hub.

#include "CFWheelHost.h"
// In the class: virtual void GatherCrushformWheelContacts_Implementation(TArray<FCFWheelContact>& OutContacts) const override;
void AMyVehicle::GatherCrushformWheelContacts_Implementation(TArray<FCFWheelContact>& OutContacts) const
{
// One entry per wheel, WheelIndex stable from frame to frame.
}

UCFPropContactComponent asks it. It decides what breaks and where the report goes. It is useful for a trailer, whose contacts break props through its tractor. Every answer has a default that describes a body with no host:

EventAnswersDefault
IsBreakerSimulatedHereECFHostAnswer: does this machine simulate the bodyThe authority
GetBreakerBodyThe body whose contacts break unitsNull, which is the owner’s root primitive
GetBreakReportRouteThe UCFPropContactComponent whose connection carries the break reportsNull, which is the owner’s own component
GetBreakerDeformThe UCFDeformComponent that a broken unit dentsNull: a broken unit never dents the body

Blueprint. Add the interface and implement the events you need. The graph below implements IsBreakerSimulatedHere. It prints when the event runs, and it leaves the answer at Default.

The graph implements IsBreakerSimulatedHere and leaves the answer at Default.

C++. The override for a trailer that reports through its tractor:

#include "CFBreakerHost.h"
UCFPropContactComponent* ATrailer::GetBreakReportRoute_Implementation() const
{
return TractorContact;
}

UCFTireComponent asks it for the wheels of a vehicle, and writes the tyre offsets and pushes back to it. The host keeps no tyre state. The component remembers what it changed and gives every offset back when it goes away. There are four events:

EventAnswersDefault
GatherCrushformTireWheelsArray of FCFTireWheelDesc: the wheel’s mesh, its PhysicsRadius, its HostIndex, its VisualOffset and bAcceptsPushNothing, which keeps the tyres round
SetCrushformTireWheelOffsetsReceives the offsets the component changed, in one call: each OffsetCm and each PushCmNothing
GatherCrushformTireLoadRatiosContact force over nominal load, by HostIndex. An empty array means no force modelEmpty
GatherCrushformTireRigThe actors coupled with this one, itself includedJust this actor

Blueprint. Add the interface and implement the gather events as functions. The graph below implements GatherCrushformTireWheels. It prints when the event runs, and it leaves the array empty.

The graph implements GatherCrushformTireWheels and leaves the array empty.

C++. The two gathers on a vehicle:

#include "CFTireHost.h"
void AMyVehicle::GatherCrushformTireWheels_Implementation(TArray<FCFTireWheelDesc>& OutWheels) const
{
// One entry for each wheel that has a mesh, in a stable order. HostIndex is the index you give the component back.
}
void AMyVehicle::GatherCrushformTireLoadRatios_Implementation(TArray<float>& OutRatios) const
{
// Contact force over nominal load, by HostIndex. Leave the array empty when you have no force model.
}

The push. A wheel with bAcceptsPush set is moved by its PushCm when its rim would otherwise enter an obstacle, so the tyre dents around the obstacle instead of the rim. Set bAcceptsPush only when SetCrushformTireWheelOffsets applies PushCm to the wheel mesh in the same call, in the owner’s space, and the mesh keeps that push until the next call. A host that ignores the push makes the push run up to the profile’s MaxPush. A host that lets it go stale, because something else moves the mesh or turns its parent against the owner, puts the dent off its place. The rim radius comes from the RimRadius parameter of the tyre material, or from the profile’s RimRadiusOverride; a wheel with neither is never pushed.

The adapter. UCFVehicleAdapterComponent applies the push itself and reports bAcceptsPush for its wheels until a Blueprint overrides SetCrushformTireWheelOffsets. A Blueprint that overrides it places the wheels itself and gets no push. A C++ subclass that overrides SetCrushformTireWheelOffsets_Implementation has the same duty as any host: call the base, which applies PushCm, or apply the push yourself, or override BuildTireWheels to report bAcceptsPush as false. The base converts the push to the wheel’s parent space once, when it arrives. A subclass whose wheel’s parent turns against the owner, such as a steering knuckle, or whose game rewrites the wheel’s relative location, no longer carries the push the tyre subtracts, so it reports bAcceptsPush as false.

A pawn implements ICFTireViewer to tell the tyre budget which vehicle the player rides in, so the budget knows the viewer’s own vehicle. The event returns the vehicle, or null while the pawn is on foot.

Blueprint. Add the interface to the pawn. The graph below implements GetCrushformTireViewedHost on the pawn. It prints when the event runs, and it returns null.

The graph implements GetCrushformTireViewedHost on the pawn and returns null.

C++.

#include "CFTireViewer.h"
AActor* AMyPawn::GetCrushformTireViewedHost_Implementation() const
{
return Vehicle.Get();
}

UCFEngineAudioComponent asks it once a frame for the engine. Its answer is an FCFEngineState:

FieldMeaning
RunStateOff, Cranking or Running
RPMThe engine speed. The sound hears a negative speed as its magnitude
IdleRPMThe speed a running engine is never heard below
MaxRPMThe highest speed the sound follows. Zero means no limit
LoadThrottle, 0 to 1

The default is an engine that is off. The vehicle adapter implements this interface as well.

Blueprint. Add the interface and implement GetCrushformEngineState as a function that returns the struct. The graph below implements it. It prints when the event runs, and it returns the default engine state, an engine that is off.

The graph implements GetCrushformEngineState and returns the default engine state, an engine that is off.

C++.

#include "CFEngineHost.h"
FCFEngineState AMyVehicle::GetCrushformEngineState_Implementation() const
{
FCFEngineState State;
State.RunState = bEngineRunning ? ECFEngineRunState::Running : ECFEngineRunState::Off;
State.RPM = EngineRPM;
State.IdleRPM = IdleRPM;
State.MaxRPM = MaxRPM;
State.Load = Throttle;
return State;
}

ICFBreakableHost and ICFBreakableUnitPart are declared with CannotImplementInterfaceInBlueprint. Implement them in C++ only. They belong to breakable debris, and the members of both are listed in the API reference.

  • In a non-Shipping build, run Crushform.Debug 1 and hit the body with Crushform.DebugImpact. Damaged nodes appear where the body dented, which means the deform host answered.
  • Run Play In Editor and look at the Output Log for the error of a refused host and the warning of a replaced one. A clean log means each host was accepted.
  • With the wheel host implemented and the surfaces set up, drive through a mud strip. Dirt collects on the wheels and the body.

The host is refused, and the log names the interface

Section titled “The host is refused, and the log names the interface”

Cause. The object does not implement the interface, so the registry refuses it. The log says:

Host %s does not implement %s, so it cannot answer for %s

Fix. In Blueprint, add the interface in Class Settings > Implemented Interfaces > Add. In C++, add the interface to the class’s base list and override the events.

Cause. Either the wheel host gathers no wheels, or the project has no surfaces asset. With no surfaces asset, no body gets dirty. A host whose gather event is not implemented returns an empty list, which is the default.

Fix. Implement GatherCrushformWheelContacts so that it adds one FCFWheelContact for each wheel. Set Surfaces in Project Settings > Plugins > Crushform.

A call to an interface event fails in the editor

Section titled “A call to an interface event fails in the editor”

Cause. The event was called by name on an interface pointer, whose generated body is a check.

Fix. Call it through Execute_, as Your own vehicle shows, or use a message node in Blueprint.