FFB Input Provider (Steering Wheel Support)
FFBInputProvider adds steering wheel support with force feedback (FFB) to NWH Vehicle Physics 2 using the DirectInput API. It works with any DirectInput-compatible device including Logitech, Thrustmaster, Fanatec, Moza, Simagic, Simucube, and other manufacturers.
Warning: Windows only - DirectInput does not exist on other platforms. The target vehicle must have at least one steered wheel with a
WheelController; a single steered wheel is supported for motorcycles.
The force sent to the wheel comes from the tire model, not preset effects. Self-aligning torque comes from the tires' aligning moment in the physics step, so the wheel lightens as the front tires approach the grip limit and gains weight again as they recover.
Compatible Devices
All DirectInput-compatible devices work, including:
| Device | Status |
|---|---|
| Simucube 2 Pro/Ultimate | Verified |
| Fanatec DD1/DD2/CSL DD | Verified |
| Moza R9 | Verified |
| Simagic Alpha | Verified |
| Logitech G29/G920/G923/PRO | Verified |
| Thrustmaster TX/T300 | Verified |
Setup
With NWH Vehicle Physics 2 installed, the demo needs two imports: its Direct Input FFB Sample and the Unity-DirectInput Git package below. Unity-DirectInput supplies the DirectInput and force feedback libraries. The NVP2 sample contains the scene, vehicle, driving area, camera, and FFB components. No other samples are required.
Use Windows with a Windows 64-bit profile active in File > Build Profiles. Input System and URP are installed with Vehicle Physics 2; enable Input System Package (New) or Both under Player > Active Input Handling if needed.
1. Install Unity-DirectInput Package
Add the Unity-DirectInput package via Unity Package Manager:
- Open Window > Package Manager
- Click the + button and select Add package from git URL
- Enter:
https://github.com/arescec/Unity-DirectInput-NVP2.git#v1.2.1 - Click Install
- Restart Unity after installation
Use the full URL, including #v1.2.1, to install the compatible library release. Git must be installed and available on your PATH for Package Manager to install Git packages.
2. Import the Direct Input FFB Sample
In Window > Package Manager, select NWH Vehicle Physics 2, expand Samples, and import Direct Input FFB Sample. You can also import this sample before installing Unity-DirectInput; its scripts activate automatically once the dependency is installed. No scripting defines or assembly reference edits are needed.
Upgrade note: do not import the old DirectInput package sample named NWH Vehicle Physics 2 v13 FFB Sample Demo. It contains the retired v13 provider. Unity-DirectInput 1.2.1 contains only the libraries; import the current sample from NVP2.
3. Open the Demo Scene
Open Scenes/NVP2 FFB Demo.unity in the imported sample under Assets/Samples/NWH Vehicle Physics 2/<version>/Direct Input FFB Sample/.
The scene already contains the vehicle, DirectInputManager, and FFBInputProvider. Configure your controls and wheelbase settings below, then enter Play mode. The manager selects the first connected FFB-capable device. Use its FFB Device Search Term to prefer a particular wheel when several are connected.
4. Configure Input Mappings
Steering and the remaining controls use two different lookup paths:
steeringAxisis the physical DirectInput axis name read directly from the selected FFB device. Its sample default isX; it is not aDIInputManager.inputMappingskey.throttleAxis,brakeAxis,clutchAxis,handbrakeAxis, and all button fields are logical mapping names. Each non-empty field must match amappingNameonDIInputManager; set that mapping's Input Name to the physical axis or button reported by your device.
The sample provider uses the logical names throttle, brake, and clutch. Its analog
handbrake field is empty by default; set it to handbrake if you configure that sample
mapping. The sample manager contains several common pedal, sequential-shifter, and
H-pattern mappings, but their physical Input Name values are intentionally blank
because those names vary by device.
The provider also exposes optional logical names such as neutral, gears 8 and 9,
alternate shift buttons, and handbrakeButton. The sample manager does not contain
every optional mapping. Add a mapping with the same name before using one of those
fields. A misspelled or unconfigured logical name produces no input.
5. Set Wheelbase Torque and Check Polarity
Set wheelbaseTorque to your device's actual peak torque before anything else - every force in the system is scaled against it.
Then check the direction of the force. Output polarity is not the same on every device: DirectInput does not define which sign means "push left", so roughly half of all wheels need the output inverted. Drive slowly and turn:
- Correct: the wheel resists the turn and tries to return to centre.
- Wrong: the wheel pulls itself further into the turn and runs to full lock.
If it is wrong, enable invertFFBOutput. Do not try to correct it with negative coefficients elsewhere - that inverts the detail effects along with the main force.
invertFFBOutput, softLockStrength, softLockRampDeg, and collisionFullScaleDeltaV are available in the regular Force Feedback Settings inspector section.
Using FFB in Your Own Scene
Add the sample's DirectInputManager and FFBInputProvider prefabs, then assign your vehicle to the provider's Vehicle Controller field. Optionally add FFBVehicleSettings to that vehicle for per-vehicle tuning.
The prefabs are separate: FFBInputProvider does not contain a DIInputManager. Keep exactly one manager in the scene before entering Play mode. Keep the scene input provider used by your cameras, vehicle switching, or UI.
FFBInputProvider Settings
The sections below match the inspector layout.
Core Settings
| Setting | Description |
|---|---|
vehicleController |
Vehicle to read forces from. Follows the active vehicle if a VehicleChanger is present. |
useDirectInput |
Sets Steering.useRawInput, bypassing the Steering component's angle-curve, steering-rate, and speed-sensitivity processing. VehicleInputHandler smoothing is separate; for direct wheel response set its Steering Smoothing Time and Steering Return Time to 0. |
hShifterUse34asDR |
Uses H-pattern positions 3 and 4 as Drive and Reverse, for an automatic gearbox driven with a shifter. |
Hardware Configuration
| Setting | Description |
|---|---|
wheelbaseTorque |
Peak torque of your wheel in Nm. G29/G923 = 2-3, T300 = 4-5, CSL DD = 5-8, Moza R9 = 9, DD1/DD2 = 15-20, Simucube 2 Pro = 25. Set this correctly first - it is the reference every other force is scaled against. |
deviceRotationRange |
Physical rotation range the wheel is configured for in its driver, in degrees (typically 900). Must match the driver setting or the steering angle will not line up. |
steeringSensitivity |
Multiplier on steering input. Leave at 1 for a 1:1 wheel. |
Steering Geometry
These two settings control how much steering weight comes from vertical load rather than lateral force. This load-based weight remains during a slide and decreases over a crest, so the driver feels changes in road load as well as tire grip.
| Setting | Description |
|---|---|
casterAngle |
Caster in degrees. 3-7 for road cars, 5-8 for rally. Overridden per vehicle by FFBVehicleSettings.casterAngle when that is above 0. |
kingpinOffset |
Lateral distance from the kingpin axis to the contact patch, in metres. Drives parking effort and the lightening felt over a crest. |
Force Feedback Settings
| Setting | Description |
|---|---|
overallEffectStrength |
Master multiplier. 0 disables force feedback. |
forceFeedbackEffect |
Curve scaling FFB strength against steering angle. |
springEffect |
Curve scaling the DirectInput spring effect against steering angle. |
springEffectStrength |
DirectInput spring (centring) effect. 0 by default - centring already comes from the tire model. |
lowSpeedFriction |
Steering resistance while stationary or nearly so. |
friction |
Steering resistance at speed. |
centeringForceStrength |
Legacy suspension-based centring. |
centeringForceOffset |
Offsets the centre by the drift angle, so the wheel centres on the direction of travel rather than straight ahead. |
collisionEffectMultiplier |
Strength of the impact jolt. |
High-Rate Output and Mixing
Force is sent to the device from a dedicated thread rather than once per rendered frame, so kerb and grip-limit detail is not tied to your framerate.
| Setting | Description |
|---|---|
ffbThreadHz |
Output rate of the FFB thread. The default 333 Hz covers the 40-150 Hz band where kerb, rut and grip-limit detail live. Lower it only if you are chasing CPU. |
textureBudget |
Ceiling on the combined road-texture effects as a fraction of full force, before mixing with the self-aligning torque. Keeps detail from drowning out the main force. |
softClipKnee |
Force is linear up to this fraction of full scale, then compresses smoothly instead of clipping flat. 1 = hard clip. |
maxSlewNmPerSec |
Limit on how fast output torque may change, in Nm/s. Bounds impact spikes a real steering rack could not produce. 0 = unlimited. |
Damping and Centering
| Setting | Description |
|---|---|
damperStrength |
Velocity damper strength. Stabilises the wheel without attenuating the tire signal. |
damperFadeSpeed |
Vehicle speed (m/s) at which the damper drops to its high-speed floor. |
damperHighSpeedFloor |
Damper multiplier at and above the fade speed. This is the steering "drag" felt at speed - too low and a direct-drive wheel feels like it is freewheeling, too high and road detail smears. |
frictionCoeffAmount |
Small constant DirectInput friction so the wheel never feels loose. Fades as the tires start to slide. |
suspensionCenteringStrength |
Legacy suspension-difference centring offset. 0 by default; useful only on low-torque belt or gear wheels. |
Texture Effects
| Setting | Description |
|---|---|
roadTextureStrength |
Strength of road texture. Derived by high-pass filtering the real rack force, so it is automatically correct for surface and speed. 0 = off. |
Note: earlier versions had separate kerb, scrub, engine-resonance and ABS oscillators. Those were fixed-frequency tones advanced once per frame, which aliases - a 55 Hz kerb tone folds down to about 5 Hz at 60 fps, and its pitch changes with framerate. They are replaced by this single term.
Suspension Feedback
| Setting | Description |
|---|---|
suspensionBumpStrength |
Kerb, pothole and road-surface feel through the steering. |
suspensionBumpThreshold |
Minimum suspension velocity (m/s) before bump feedback triggers. Lower is more sensitive. |
Input Inversion, Axes and Deadzones
flipSteeringInput, flipThrottleInput, flipBrakeInput, flipClutchInput and flipHandbrakeInput invert a pedal or axis that reads backwards. The pedal flips default to on, which suits most wheels - a pedal that reads full when released usually needs its flip turned off, not on.
steeringAxis0to1 tells the provider whether the steering axis reports 0-1 (most devices) or already reports -1 to 1.
Deadzones (throttleDeadzone, brakeDeadzone, clutchDeadzone, handbrakeDeadzone, steeringDeadzone) trim noise around the resting position of worn pedals. Steering deadzone defaults to 0 and should normally stay there.
steeringAxis is the selected FFB device's physical DirectInput axis name. The pedal,
handbrake, and shifter fields are logical DIInputManager mapping names, as described
in setup step 4.
Output Direction, Soft Lock, and Collision Scaling
| Setting | Description |
|---|---|
invertFFBOutput |
Flips the sign of the force sent to the device. See step 5 of Setup. |
softLockStrength |
Strength of the end stop felt past full steering lock, as a fraction of full device torque. 0 disables it. |
softLockRampDeg |
Degrees of wheel travel past lock over which the end stop ramps up to full strength. |
collisionFullScaleDeltaV |
Impact speed change (m/s) that produces a full-scale collision jolt. |
Per-Vehicle Tuning
Add FFBVehicleSettings to your VehicleController GameObject to scale FFB per vehicle. A truck and a formula car want different steering weight from the same physical wheel.
| Setting | Description |
|---|---|
overallCoeff |
Overall FFB strength for this vehicle. |
satCoefficient |
Self-aligning torque multiplier. |
mechanicalTrailCoeff |
Multiplier on the caster/kingpin load term. 0 = tire trail only (very light), 0.2 = baseline, above 1 = exaggerated weight. |
centeringCoeff |
Centring force multiplier. |
casterAngle |
Caster for this vehicle. 0 = use the provider's global value. |
kingpinOffset |
Kingpin offset for this vehicle, in metres. Negative = use global. |
wheelRotationRange |
Steering wheel rotation range for this vehicle, in degrees. |
staticFrontLoadOverride |
Measured static front axle load in Newtons, used to normalise how load modulates the rack force. 0 = estimate as half the vehicle weight. |
frictionCoeff |
At-speed friction multiplier. |
lowSpeedFrictionCoeff |
Stationary friction multiplier. |
longitudinalCoeff |
How much braking and acceleration are felt through the steering. |
scrubRadius |
Scrub radius in metres. Typical 0.01-0.03 for road cars. |
suspensionBumpStrength |
Bump feedback for this vehicle. Negative = use global. |
suspensionBumpThreshold |
Bump sensitivity for this vehicle. Negative = use global. |
Gotcha: fields whose "inherit" value is negative (
kingpinOffset,suspensionBumpStrength,suspensionBumpThreshold) treat 0 as a real value meaning off. Leave them negative unless you actually want that effect disabled on this vehicle. When upgrading from v14 Beta 2, serialized zero values for the two suspension fields are migrated automatically to the negative inherit value.
Gotcha:
wheelRotationRangehere also sets the steering ratio used to convert rack torque to torque at the rim. It is not the same field asSteering.steeringWheelTurnRatio, which only rotates the in-game steering wheel mesh and does nothing if no mesh is assigned.
Tuning by Wheel Type
Direct drive (CSL DD, R9, Alpha, Simucube). Defaults are aimed here. Set wheelbaseTorque correctly, leave springEffectStrength and suspensionCenteringStrength at 0, and tune feel with damperHighSpeedFloor - it is the single knob that decides whether the wheel feels planted or loose at speed.
Belt and gear wheels (T300, G29, G923). These have mechanical friction and less headroom. Raising springEffectStrength and suspensionCenteringStrength above 0 helps the wheel find centre, and lowering textureBudget keeps detail from eating the limited torque available for the main force.
Clipping. If _compressionPct sits high in the debug readout, the soft clip is working constantly and detail is being squashed. Lower overallEffectStrength or the per-vehicle overallCoeff rather than raising wheelbaseTorque above the device's real figure.
Diagnostics
In play mode the inspector shows a Debug Values section:
| Readout | Meaning |
|---|---|
_signedSatNm |
Signed steering-column torque in Nm. Use this to confirm polarity: turning right should give a torque pushing back toward centre. |
_primaryForcePct |
Share of output coming from the self-aligning torque. |
_effectsForcePct |
Share coming from texture and secondary effects. |
_totalForcePct |
Total output as a percentage of full device torque. |
_compressionPct |
How hard the soft clip is compressing. Persistently high means you are asking for more torque than the device has. |
Troubleshooting
Wheel Pulls Into the Turn Instead of Centering
Output polarity is inverted for your device. Enable Invert FFB Output in the regular
FFBInputProvider inspector.
Device Not Detected
- Ensure Unity-DirectInput package is installed
- Restart Unity after installation
- Check that your device appears in Windows Game Controllers
- Try disconnecting and reconnecting the device
- Check there is only one
DIInputManagerin the scene
Slow Enumeration
DirectInput device enumeration can occasionally take up to 60 seconds due to a Windows bug. USB Audio DACs and certain keyboards (e.g. Corsair) can cause this. Try disconnecting non-essential USB devices.
Force Feedback Not Working
- Verify
overallEffectStrengthis greater than 0 - Check
wheelbaseTorquematches your wheel's actual torque output - Ensure the vehicle has at least one steered wheel with a
WheelController. Motorcycles may use one steered wheel. - Check DIInputManager shows your device as FFB-capable
Forces Cut Out After a Pause or Script Recompile
Output fades to zero if the physics step stops publishing for 250 ms, including during pauses, domain reloads, and main-thread stalls. It recovers automatically when the game resumes.
Wheel Feels Numb or Over-Damped
Lower damperHighSpeedFloor first. If the wheel then oscillates around centre, raise it back slightly rather than adding spring or centring force.
Extending the Provider
If you are modifying FFBInputProvider, the output loop runs on its own thread. No UnityEngine API may be called from it - not Time, not AnimationCurve.Evaluate, not any UnityEngine.Object access, not Debug.Log. Mathf is safe because it is pure static maths. Anything else must be sampled during FixedUpdate and passed across in the snapshot struct the thread reads. Breaking this rule hangs the editor rather than producing a warning.
Requirements
- Unity-DirectInput package 1.2.1, installed with the full Git URL in setup step 1
- NWH Vehicle Physics 2 Direct Input FFB Sample
- Windows 10/11 with a Windows 64-bit Build Profile active
- DirectInput-compatible steering wheel with force feedback
- Vehicle must have at least one steered wheel with a
WheelController; a single steered wheel is supported