Add haptics example documentation for Project Void controller feedback

- 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.
This commit is contained in:
Lefteris Notas
2026-05-22 17:16:34 +03:00
parent 7c2e8df6b1
commit 14441c000c
16 changed files with 1213 additions and 46 deletions

View File

@@ -0,0 +1,438 @@
# Haptics Example — Project Void Controller Feedback
**Version:** 1.0 | **Target UE:** 5.55.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
1. Navigate to `Content/Game/DataAssets/Haptics/`
2. Right-click → **Miscellaneous → Data Asset**
3. Select class: `DA_HapticProfile`
4. Name: `DA_Haptic_{Event}` (see table below)
5. Open the asset → fill in the fields:
- **ProfileTag:** The GameplayTag for this effect (e.g., `Haptic.Damage.Light`)
- **EventType:** Select from `EHapticEvent` enum
- **ForceFeedbackEffect:** Assign the `UForceFeedbackEffect` waveform curve asset
- **IntensityCurve:** Optional `UCurveFloat` for 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
### 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 6090 (calm/tense) |
| `DA_Haptic_Heartbeat_Fast` | `Haptic.Heartbeat.Fast` | Heartbeat | 0.08s | Left | 40 | BPM 90140 (scared) |
| `DA_Haptic_Heartbeat_Panic` | `Haptic.Heartbeat.Panic` | Heartbeat | 0.05s | Both | 50 | BPM 140180 (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`:
1. Navigate to `Content/Game/DataAssets/Haptics/Curves/`
2. Right-click **Miscellaneous → Force Feedback Effect**
3. Name: `FFE_{Event}` (e.g., `FFE_Damage_Light`)
4. 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
5. 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 pattern
- `FFE_Shotgun_PS5` trigger kick + body rumble simultaneously
- `FFE_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)
1030: 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 40180
→ Select profile:
BPM 4090: Haptic.Heartbeat.Normal (slow pulse on left motor)
BPM 90140: Haptic.Heartbeat.Fast (faster pulse, stronger)
BPM 140180: 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:**
1. Create `USoundWave` or `UMetaSoundSource` assets
2. Route through `SS_AudioManager` with `Bus = Dialogue` (or a dedicated `Speaker` sub-bus)
3. `SS_AudioManager` detects 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).*