# 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 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 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`: 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) 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:** 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).*