- Introduced comprehensive guide for setting up controller haptics and force feedback. - Detailed directory structure for haptic profiles and creation steps for DA_HapticProfile instances. - Included platform-specific configurations for Xbox and PS5 DualSense adaptive triggers. - Outlined wiring of BPC_HapticsController to various gameplay systems and events. - Provided accessibility integration options and testing checklist for haptic functionality.
17 KiB
Haptics Example — Project Void Controller Feedback
Version: 1.0 | Target UE: 5.5–5.7 | Framework System: BPC_HapticsController (148) + DA_HapticProfile (121)
Purpose
This document walks through setting up controller haptics/force feedback in the Project Void horror game prototype. It covers creating DA_HapticProfile Data Asset instances for every gameplay event, wiring BPC_HapticsController to all relevant systems, and platform-specific tuning for Xbox rumble and PS5 DualSense adaptive triggers.
Rule: All game haptic content lives in Content/Game/DataAssets/Haptics/. Never modify Content/Framework/Settings/BPC_HapticsController.
Game Haptics Directory Structure
Content/Game/DataAssets/
├── Haptics/ ← ALL game haptic profiles
│ ├── DA_Haptic_Damage_Light.uasset
│ ├── DA_Haptic_Damage_Heavy.uasset
│ ├── DA_Haptic_Heartbeat_Normal.uasset
│ ├── DA_Haptic_Heartbeat_Fast.uasset
│ ├── DA_Haptic_Weapon_Pistol.uasset
│ ├── DA_Haptic_Weapon_Shotgun.uasset
│ ├── DA_Haptic_Weapon_Crowbar.uasset
│ ├── DA_Haptic_Reload.uasset
│ ├── DA_Haptic_Footstep_Tile.uasset
│ ├── DA_Haptic_Footstep_Wood.uasset
│ ├── DA_Haptic_Footstep_Concrete.uasset
│ ├── DA_Haptic_Explosion.uasset
│ ├── DA_Haptic_Pickup_Item.uasset
│ ├── DA_Haptic_Pickup_Weapon.uasset
│ ├── DA_Haptic_Grab_Object.uasset
│ ├── DA_Haptic_Release_Throw.uasset
│ ├── DA_Haptic_Scare_JumpScare.uasset
│ ├── DA_Haptic_Scare_TensionRise.uasset
│ ├── DA_Haptic_Ambient_Void.uasset
│ ├── DA_Haptic_LowHealth.uasset
│ ├── DA_Haptic_StaminaExhaust.uasset
│ ├── DA_Haptic_Death.uasset
│ ├── DA_Haptic_UI_Confirm.uasset
│ └── DA_Haptic_UI_Navigate.uasset
Step 1: Create DA_HapticProfile Data Asset Instances
For each event below, create a Data Asset instance in the Content Browser.
How to Create a Haptic Profile
- Navigate to
Content/Game/DataAssets/Haptics/ - Right-click → Miscellaneous → Data Asset
- Select class:
DA_HapticProfile - Name:
DA_Haptic_{Event}(see table below) - Open the asset → fill in the fields:
- ProfileTag: The GameplayTag for this effect (e.g.,
Haptic.Damage.Light) - EventType: Select from
EHapticEventenum - ForceFeedbackEffect: Assign the
UForceFeedbackEffectwaveform curve asset - IntensityCurve: Optional
UCurveFloatfor intensity over time - Duration: Total effect seconds
- MotorMask: Left, Right, or Both
- Priority: 0 (lowest, like footsteps) to 100 (highest, like death)
- bCanInterrupt: Whether lower-priority effects can interrupt this
- PlatformMinIntensity: Minimum intensity before effect is felt
- ProfileTag: The GameplayTag for this effect (e.g.,
Complete Haptic Profile Data Table
| Asset Name | GameplayTag | EventType | Duration | Motor | Priority | Trigger Condition |
|---|---|---|---|---|---|---|
DA_Haptic_Damage_Light |
Haptic.Damage.Light |
Damage | 0.15s | Both | 60 | Player takes ≤10 damage |
DA_Haptic_Damage_Heavy |
Haptic.Damage.Heavy |
HeavyDamage | 0.4s | Both | 80 | Player takes >30 damage |
DA_Haptic_Damage_Critical |
Haptic.Damage.Critical |
HeavyDamage | 0.6s | Both | 90 | Player HP <10% and hit |
DA_Haptic_Heartbeat_Normal |
Haptic.Heartbeat.Normal |
Heartbeat | 0.1s | Left | 30 | BPM 60–90 (calm/tense) |
DA_Haptic_Heartbeat_Fast |
Haptic.Heartbeat.Fast |
Heartbeat | 0.08s | Left | 40 | BPM 90–140 (scared) |
DA_Haptic_Heartbeat_Panic |
Haptic.Heartbeat.Panic |
Heartbeat | 0.05s | Both | 50 | BPM 140–180 (panic) |
DA_Haptic_Weapon_Pistol |
Haptic.WeaponFire.Pistol |
WeaponFire | 0.08s | Right | 50 | Pistol fired |
DA_Haptic_Weapon_Shotgun |
Haptic.WeaponFire.Shotgun |
WeaponFire | 0.25s | Both | 70 | Shotgun fired |
DA_Haptic_Weapon_Crowbar |
Haptic.MeleeImpact.Crowbar |
MeleeImpact | 0.15s | Both | 55 | Crowbar hits enemy |
DA_Haptic_Reload |
Haptic.WeaponReload |
WeaponReload | 0.1s | Right | 40 | Reload magazine click |
DA_Haptic_Footstep_Tile |
Haptic.Footstep.Tile |
Footstep | 0.02s | Left | 10 | Walking on tile floor |
DA_Haptic_Footstep_Wood |
Haptic.Footstep.Wood |
Footstep | 0.03s | Left | 10 | Walking on wood floor |
DA_Haptic_Footstep_Concrete |
Haptic.Footstep.Concrete |
Footstep | 0.04s | Both | 10 | Walking on concrete |
DA_Haptic_Explosion |
Haptic.Explosion |
Explosion | 0.6s | Both | 85 | Nearby explosion |
DA_Haptic_Pickup_Item |
Haptic.Pickup.Item |
PickupItem | 0.05s | Right | 20 | Item picked up |
DA_Haptic_Pickup_Weapon |
Haptic.Pickup.Weapon |
PickupItem | 0.1s | Both | 25 | Weapon equipped |
DA_Haptic_Grab_Object |
Haptic.Grab |
GrabObject | 0.08s | Both | 35 | Physics object grabbed |
DA_Haptic_Release_Throw |
Haptic.Release.Throw |
ReleaseObject | 0.12s | Both | 35 | Physics object thrown |
DA_Haptic_Scare_JumpScare |
Haptic.Scare.JumpScare |
ScareEvent | 0.5s | Both | 95 | Jump scare triggers |
DA_Haptic_Scare_TensionRise |
Haptic.Scare.TensionRise |
ScareEvent | 2.0s | Left | 65 | Ambient tension building |
DA_Haptic_Ambient_Void |
Haptic.Ambient.Void |
AmbientPulse | 3.0s | Left | 15 | Void Space ambient rumble |
DA_Haptic_LowHealth |
Haptic.LowHealth |
LowHealth | 0.2s | Both | 75 | HP drops below 25% |
DA_Haptic_StaminaExhaust |
Haptic.StaminaExhausted |
StaminaExhausted | 0.3s | Both | 45 | Stamina reaches zero |
DA_Haptic_Death |
Haptic.Death |
Death | 0.8s | Both | 100 | Player dies |
DA_Haptic_UI_Confirm |
Haptic.UI.Confirm |
UI_Confirm | 0.03s | Right | 5 | Menu option selected |
DA_Haptic_UI_Navigate |
Haptic.UI.Navigate |
UI_Navigate | 0.02s | Right | 5 | Menu cursor moved |
Creating ForceFeedbackEffect Assets (Waveform Curves)
For each haptic profile that uses a UForceFeedbackEffect:
- Navigate to
Content/Game/DataAssets/Haptics/Curves/ - Right-click → Miscellaneous → Force Feedback Effect
- Name:
FFE_{Event}(e.g.,FFE_Damage_Light) - Open the asset:
- Add a channel: Left Large (low frequency rumble motor)
- Add a channel: Right Small (high frequency precision motor)
- For the Damage Light curve: short spike at 0.5 intensity, 0.15s duration
- For the Shotgun curve: heavy dual-motor spike, 0.25s duration
- For the Heartbeat curve: single low-frequency pulse at 0.1s
- Assign the FFE asset to the corresponding
DA_HapticProfile
Platform-Specific Force Feedback Assets
For PS5 DualSense-only effects, create separate FFE assets in Curves/PS5/:
FFE_Damage_Heavy_PS5— higher fidelity trigger and haptic patternFFE_Shotgun_PS5— trigger kick + body rumble simultaneouslyFFE_Heartbeat_PS5— pulse on both adaptive triggers
The BPC_HapticsController selects the right asset at runtime based on CurrentPlatform.
Step 2: Wiring BPC_HapticsController to Gameplay Systems
2.1 Player Damage → Haptic
In BP_HorrorPlayerCharacter (or wherever BPC_HealthSystem is attached):
[In BPC_HealthSystem: OnTakeDamage Event]
→ Get BPC_HapticsController from Owner (PlayerController)
→ Branch on damage amount:
≤10: PlayHapticByTag(Haptic.Damage.Light, 1.0)
10–30: PlayHapticByTag(Haptic.Damage.Heavy, 1.0)
>30: PlayHapticByTag(Haptic.Damage.Critical, 1.0)
→ Branch on current health / max health:
<0.25: PlayHapticByTag(Haptic.LowHealth, 1.0)
2.2 Weapon Fire → Haptic
In BP_Pistol_Held and BP_Shotgun_Held (or the BPC_FirearmSystem):
[In BPC_FirearmSystem: OnFire Event]
→ Get Owner PlayerController → Get BPC_HapticsController
→ Switch on equipped weapon:
Pistol: PlayHapticByTag(Haptic.WeaponFire.Pistol, 1.0)
Shotgun: PlayHapticByTag(Haptic.WeaponFire.Shotgun, 1.0)
For DualSense adaptive triggers (PS5 only — no-op on other platforms):
[AimDownSights pressed]
→ if IsDualSenseConnected:
→ SetDualSenseTriggerEffect("Right", "WeaponFire", 2, 5) ← R2 stiffens at position 2
[AimDownSights released]
→ if IsDualSenseConnected:
→ SetDualSenseTriggerEffect("Right", "Resistance", 0, 0) ← Remove trigger resistance
2.3 Reload → Haptic
[In BPC_ReloadSystem: ReloadComplete Event]
→ Get BPC_HapticsController
→ PlayHapticByTag(Haptic.WeaponReload, 1.0)
2.4 Melee → Haptic
[In BPC_MeleeSystem: OnMeleeHit Event]
→ Get BPC_HapticsController
→ PlayHapticByTag(Haptic.MeleeImpact.Crowbar, 1.0)
2.5 Heartbeat → Continuous Haptic
In PC_HorrorController (child of PlayerController with BPC_HapticsController attached):
[Event BeginPlay]
→ Get Pawn → Get BPC_StateManager
→ Bind Event OnHeartRateChanged → [Custom Event]
[Custom Event: OnHeartRateChanged(BPM)]
→ Get BPC_HapticsController (self)
→ PlayHeartbeatHaptic(BPM) ← This starts/stops the looping heartbeat pulse
The heartbeat haptic automatically switches profiles based on BPM range:
In PlayHapticByTag (heartbeat):
→ Clamp BPM to 40–180
→ Select profile:
BPM 40–90: Haptic.Heartbeat.Normal (slow pulse on left motor)
BPM 90–140: Haptic.Heartbeat.Fast (faster pulse, stronger)
BPM 140–180: Haptic.Heartbeat.Panic (both motors, double-pulse)
2.6 Footsteps → Surface-Dependent Haptic
In GASP animation notifies or BPC_MovementStateSystem:
[Animation Notify: Footstep]
→ Physical Surface Trace → Get Surface Type (Tile, Wood, Concrete, Carpet, Metal)
→ Get BPC_HapticsController
→ Switch on Surface:
Tile: PlayHapticByTag(Haptic.Footstep.Tile, surfaceDependentIntensity)
Wood: PlayHapticByTag(Haptic.Footstep.Wood, surfaceDependentIntensity)
Concrete: PlayHapticByTag(Haptic.Footstep.Concrete, surfaceDependentIntensity)
Metal: PlayHapticByTag(Haptic.Footstep.Metal, surfaceDependentIntensity)
Carpet: Skip (no haptic on soft surfaces)
→ Scale intensity by movement speed:
Sneak (0.3x), Walk (1.0x), Sprint (1.5x)
2.7 Scare Events → Haptic
[In BPC_ScareEventSystem: OnScareTriggered(ScareType)]
→ Get BPC_HapticsController
→ Switch on ScareType:
JumpScare: PlayHapticByTag(Haptic.Scare.JumpScare, 1.0)
TensionRise: PlayHapticByTag(Haptic.Scare.TensionRise, 1.0)
→ This plays a 2s ramp-up rumble on the left motor
2.8 Void Space Ambient → Continuous Tension Rumble
[In BP_AtmosphereController_WardA: EnterVoidSpace Event]
→ Get BPC_HapticsController
→ PlayHapticByTag(Haptic.Ambient.Void, 0.6) ← Low-intensity ambient rumble
[ExitVoidSpace Event]
→ Get BPC_HapticsController
→ StopHaptic ← Stop all ambient
2.9 Death → Final Rumble
[In BPC_DeathHandlingSystem: OnDeath Event]
→ Get BPC_HapticsController
→ PlayHapticByTag(Haptic.Death, 1.0)
2.10 Stamina Exhausted → Warning Pulse
[In BPC_StaminaSystem: Stamina = 0 Event]
→ Get BPC_HapticsController
→ PlayHapticByTag(Haptic.StaminaExhausted, 1.0)
2.11 UI Navigation → Subtle Clicks
[In WBP_MainMenu or WBP_PauseMenu: OnButtonHovered / OnSelectionChanged]
→ Get BPC_HapticsController (from owning PlayerController)
→ PlayHapticByTag(Haptic.UI.Navigate, 1.0)
[OnButtonPressed / Confirm]
→ PlayHapticByTag(Haptic.UI.Confirm, 1.0)
Step 3: Platform-Specific Configuration
3.1 Xbox Controller Settings
Xbox controllers use standard dual-motor rumble (Left = low frequency, Right = high frequency). No special setup required beyond creating UForceFeedbackEffect assets.
For Xbox-specific tuning:
- Left motor (low frequency): Use for heavy impacts, explosions, death
- Right motor (high frequency): Use for weapon fire, reload clicks, UI feedback
- Both motors: Use for damage, jump scares, melee impacts
3.2 PS5 DualSense Adaptive Triggers
The DualSense supports two trigger effect types via the PS5 Controller Plugin:
| Effect Type | Description | Use Case |
|---|---|---|
Resistance |
Trigger stiffens at a certain press point | Aiming down sights (R2) |
Vibration |
Trigger motor vibrates | Weapon fire kick on R2 |
WeaponFire |
Simulated trigger break | Pulling trigger on pistol/shotgun |
BowDraw |
Increasing resistance as trigger pulls | (future: bow weapon) |
AutomaticRifle |
Rapid trigger vibration | (future: automatic rifles) |
Trigger Effect Parameters:
StartPosition: Where on the trigger pull the effect begins (0 = fully released, 9 = fully pressed)Strength: Effect intensity (0 = off, 8 = maximum)EndPosition(Resistance only): Where the resistance wall ends
Example: Pistol trigger effect:
WeaponFire effect:
StartPosition = 4 ← Effect starts midway through pull
Strength = 6 ← Moderate break-point feel
Graceful fallback: On Xbox/PC, SetDualSenseTriggerEffect is a no-op. No crash, no error.
3.3 Controller Speaker Audio (PS5)
The DualSense has a small speaker. Use it sparingly for immersion:
| Game Event | Speaker Audio |
|---|---|
| Radio crackle | When near a working radio or walkie-talkie |
| Heartbeat | Quiet heartbeat thump when stress is high |
| Key jingle | When picking up keys |
| Flashlight click | Mechanical click when toggling flashlight |
Implementation:
- Create
USoundWaveorUMetaSoundSourceassets - Route through
SS_AudioManagerwithBus = Dialogue(or a dedicatedSpeakersub-bus) SS_AudioManagerdetects platform and routes to controller speaker on PS5, falls back to main output on other platforms
Step 4: Accessibility Integration
Settings Menu Integration
In WBP_SettingsMenu (or WBP_AccessibilityUI), add a Haptics section:
Haptics Settings:
├── [Toggle] Controller Vibration: ON/OFF
│ └─ Calls BPC_AccessibilitySettings.SetHapticsEnabled(bool)
│ └─ Dispatcher → BPC_HapticsController.SetHapticsEnabled()
├── [Slider] Vibration Intensity: 0% – 100%
│ └─ Sets BPC_HapticsController.HapticIntensityScale
├── [Toggle] Adaptive Triggers (PS5 Only): ON/OFF
│ └─ Sets BPC_HapticsController.bEnableDualSenseTriggers
└── [Toggle] Controller Speaker (PS5 Only): ON/OFF
└─ Sets BPC_HapticsController.bEnableSpeakerAudio
Accessibility Presets
Provide preset profiles:
- Full Haptics (default): All effects on, full intensity
- Reduced Haptics: Half intensity, no ambient rumble, no trigger effects
- No Haptics: All vibration off (accessibility minimum)
Step 5: Testing Checklist
Basic Functionality
- Pick up an item → controller vibrates briefly
- Fire pistol → right motor kicks
- Fire shotgun → both motors heavy kick
- Take damage → vibration intensity scales with damage amount
- Heartbeat BPM increases when enemy nearby → pulse speeds up
- Walk on tile floor → light footstep ticks on left motor
- Sprint on concrete → heavier footstep pulses on both motors
- Jump scare triggers → intense 0.5s dual-motor rumble
- Enter Void Space → low continuous ambient rumble starts
- Leave Void Space → ambient rumble stops
- Death → final heavy 0.8s rumble plays
Platform
- Xbox controller: all effects work (standard rumble)
- PS5 DualSense: adaptive triggers stiffen when aiming
- PS5 DualSense: trigger kick on weapon fire
- PS4 DualShock: falls back to standard rumble
- PC keyboard/mouse: no vibration (expected — no controller connected)
- Controller hot-swap: new platform detected, correct profiles load
Accessibility
- Toggle vibration OFF in settings → no haptic effects play
- Toggle vibration ON again → haptics resume
- Reduce intensity to 50% → all effects half strength
- Disable adaptive triggers → trigger effects stop on PS5
Edge Cases
- Rapid weapon fire → respects MinTimeBetweenEffects, no rumble spam
- Damage + weapon fire simultaneously → higher priority damage wins
- Heartbeat continues during combat — doesn't overwhelm other effects
- Controller disconnected mid-gameplay → no crash, haptics gracefully stop
- Load save file → haptics settings restored from SS_SettingsSystem
Haptic GameplayTag Reference
All tags are in the Haptic. namespace, registered in DT_Tags_Player.csv:
Haptic.Damage.Light
Haptic.Damage.Heavy
Haptic.Damage.Critical
Haptic.Heartbeat.Normal
Haptic.Heartbeat.Fast
Haptic.Heartbeat.Panic
Haptic.WeaponFire.Pistol
Haptic.WeaponFire.Shotgun
Haptic.WeaponReload
Haptic.MeleeImpact.Crowbar
Haptic.MeleeImpact.Default
Haptic.Footstep.Tile
Haptic.Footstep.Wood
Haptic.Footstep.Concrete
Haptic.Footstep.Metal
Haptic.Footstep.Gravel
Haptic.Explosion
Haptic.Pickup.Item
Haptic.Pickup.Weapon
Haptic.Grab
Haptic.Release.Throw
Haptic.Scare.JumpScare
Haptic.Scare.TensionRise
Haptic.Ambient.Void
Haptic.Ambient.Default
Haptic.LowHealth
Haptic.StaminaExhausted
Haptic.Death
Haptic.UI.Confirm
Haptic.UI.Navigate
Haptics Example v1.0 — Part of the Project Void horror game prototype. Framework systems: BPC_HapticsController (148), DA_HapticProfile (121).