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

@@ -141,7 +141,7 @@ docs/
enhanced-input-system.md enhanced-input-system.md
bpc-statemanager.md # NEW — State Manager implementation checklist bpc-statemanager.md # NEW — State Manager implementation checklist
``` ```
**Total: 147 numbered Blueprint files + 5 enums + 5 Data Assets + 11 Data Table CSVs + TEMPLATE.md + AUDIT_REPORT.md + INDEX.md + 13 developer docs + 9 architecture docs + 1 audit report = 192 files in 20 directory groups** **Total: 148 numbered Blueprint files + 5 enums + 5 Data Assets + 11 Data Table CSVs + TEMPLATE.md + AUDIT_REPORT.md + INDEX.md + 13 developer docs + 9 architecture docs + 1 audit report + 1 haptics game example = 193 files in 20 directory groups**
## Naming Conventions ## Naming Conventions
| Prefix | Type | | Prefix | Type |
@@ -193,12 +193,13 @@ docs/
- **Phase 9:** Adaptive & Atmosphere (10-adaptive, 15 systems) - **Phase 9:** Adaptive & Atmosphere (10-adaptive, 15 systems)
- **Phase 10:** Data Assets (14-data-assets, 16 systems) - **Phase 10:** Data Assets (14-data-assets, 16 systems)
- **Phase 11:** Meta/Progression (11-meta, 2 systems) - **Phase 11:** Meta/Progression (11-meta, 2 systems)
- **Phase 12:** Settings/Platform (12-settings, 2 systems) - **Phase 12:** Settings/Platform (12-settings, 3 systems — includes BPC_HapticsController 148)
- **Phase 13:** Polish (13-polish, 9 systems) - **Phase 13:** Polish (13-polish, 9 systems)
- **Phase 14:** State Management (BPC_StateManager + 4 enums + 3 structs + DA_StateGatingTable — 130, 131) - **Phase 14:** State Management (BPC_StateManager + 4 enums + 3 structs + DA_StateGatingTable — 130, 131)
- **Phase 15:** MetaSounds Audio (SS_AudioManager + BP_RoomAudioZone + DA_AudioSettings + DA_RoomAcousticPreset — 132-135) - **Phase 15:** MetaSounds Audio (SS_AudioManager + BP_RoomAudioZone + DA_AudioSettings + DA_RoomAcousticPreset — 132-135)
- **Phase 16:** Multiplayer Networking — All systems updated with `HasAuthority()` gates, `Server_` RPCs, `RepNotify` handlers, and client prediction patterns. See [`docs/architecture/multiplayer-networking.md`](docs/architecture/multiplayer-networking.md). - **Phase 16:** Multiplayer Networking — All systems updated with `HasAuthority()` gates, `Server_` RPCs, `RepNotify` handlers, and client prediction patterns. See [`docs/architecture/multiplayer-networking.md`](docs/architecture/multiplayer-networking.md).
- **Phase 17:** Planar Capture System — Mirrors, portals, monitors, horror surfaces. C++ core (136-138) + Blueprint actors (139-143) + Material/MPC (144-145) + Data Assets (146-147). See [`docs/architecture/planar-capture-system.md`](docs/architecture/planar-capture-system.md). - **Phase 17:** Planar Capture System — Mirrors, portals, monitors, horror surfaces. C++ core (136-138) + Blueprint actors (139-143) + Material/MPC (144-145) + Data Assets (146-147). See [`docs/architecture/planar-capture-system.md`](docs/architecture/planar-capture-system.md).
- **Phase 18:** Haptics Controller — BPC_HapticsController (148) for GameplayTag-driven force feedback, DualSense adaptive triggers, heartbeat pulse. See [`docs/blueprints/12-settings/148_BPC_HapticsController.md`](docs/blueprints/12-settings/148_BPC_HapticsController.md).
## Key Communication Rules ## Key Communication Rules
1. Gameplay Tags + Subsystem lookup (global, decoupled) 1. Gameplay Tags + Subsystem lookup (global, decoupled)
@@ -254,4 +255,5 @@ No PR is accepted without these files being current. This ensures the animator,
- **Phase 8-13:** COMPLETE AUDIT_REPORT updated, CONTEXT.md updated, verification, final commit - **Phase 8-13:** COMPLETE AUDIT_REPORT updated, CONTEXT.md updated, verification, final commit
- **Phase 14-15:** COMPLETE State Management (130-131) + MetaSounds Audio (132-135) blueprint specs created - **Phase 14-15:** COMPLETE State Management (130-131) + MetaSounds Audio (132-135) blueprint specs created
- **Phase 16:** IN PROGRESS Multiplayer Networking: all 135 blueprint specs being updated with replication stubs, RPC definitions, and server-authoritative patterns. Developer docs updated. Architecture doc created at [`docs/architecture/multiplayer-networking.md`](docs/architecture/multiplayer-networking.md) - **Phase 16:** IN PROGRESS Multiplayer Networking: all 135 blueprint specs being updated with replication stubs, RPC definitions, and server-authoritative patterns. Developer docs updated. Architecture doc created at [`docs/architecture/multiplayer-networking.md`](docs/architecture/multiplayer-networking.md)
- **Remaining:** Cross-reference pass across all 147 files; deprecated BPC_AudioAtmosphereController (95) references to update; Planar Capture System (Phase 17) C++ written, blueprint specs created, pending UE5 editor compile and BP child creation - **Phase 18:** COMPLETE Haptics Controller (148) blueprint spec created with full Manual Implementation Guide, DA_HapticProfile (121) enhanced, game example (`docs/game/haptics-example.md`) created. All developer docs and indexes updated.
- **Remaining:** Cross-reference pass across all 148 files; deprecated BPC_AudioAtmosphereController (95) references to update; Planar Capture System (Phase 17) C++ written, blueprint specs created, pending UE5 editor compile and BP child creation

View File

@@ -4102,7 +4102,7 @@ Build these first. Everything else depends on them.
✓ Full accessibility settings ✓ Full accessibility settings
✓ Enhanced Input — priority context stack, key rebinding ✓ Enhanced Input — priority context stack, key rebinding
✓ MetaSounds Audio — 4 bus categories, room zones, gameplay parameters ✓ MetaSounds Audio — 4 bus categories, room zones, gameplay parameters
✓ DualSense haptics + adaptive triggers ✓ DualSense haptics + adaptive triggers (BPC_HapticsController 148)
✓ Post-game meta progression and ending tracker ✓ Post-game meta progression and ending tracker
✓ Run history and summary ✓ Run history and summary
✓ Document archive + journal + lore system ✓ Document archive + journal + lore system
@@ -4113,7 +4113,7 @@ Build these first. Everything else depends on them.
✓ Physics drag/throw ✓ Physics drag/throw
✓ Subtitles + localisation hooks ✓ Subtitles + localisation hooks
✓ Hiding spots ✓ Hiding spots
✓ 16 Data Asset types (item, equipment, encounter, atmosphere, scare, etc.) ✓ 16 Data Asset types (item, equipment, encounter, atmosphere, scare, haptic, etc.)
✓ Server-authoritative multiplayer networking (HasAuthority gates, Server_ RPCs, RepNotify) ✓ Server-authoritative multiplayer networking (HasAuthority gates, Server_ RPCs, RepNotify)
✓ Central state authority — systems never check each other directly ✓ Central state authority — systems never check each other directly
``` ```
@@ -4123,5 +4123,5 @@ Build these first. Everything else depends on them.
*End of Reusable UE5 Modular Game Framework v2.0* *End of Reusable UE5 Modular Game Framework v2.0*
*Framework designed for generic use. All system names are project-agnostic.* *Framework designed for generic use. All system names are project-agnostic.*
*Override in /Game/ folder. Never modify /Framework/ core assets.* *Override in /Game/ folder. Never modify /Framework/ core assets.*
*C++ source: 22 classes in Source/PG_Framework/ | BP specs: 135 numbered files in docs/blueprints/* *C++ source: 22 classes in Source/PG_Framework/ | BP specs: 148 numbered files in docs/blueprints/*
*Companion docs: docs/checklists/ (build order, status), docs/developer/ (per-category references), docs/architecture/ (state management, audio, networking, animation, sound)* *Companion docs: docs/checklists/ (build order, status), docs/developer/ (per-category references), docs/architecture/ (state management, audio, networking, animation, sound)*

View File

@@ -0,0 +1,577 @@
# 148 — Haptics Controller (`BPC_HapticsController`)
> **Blueprint-Only Implementation** — UE 5.55.7 fully supports controller haptics/force feedback from Blueprints. No C++ required. This component wraps UE5's `Play Force Feedback`, `Set Haptics By Value`, and DualSense adaptive trigger APIs behind a GameplayTag-driven event system.
---
## Purpose
Abstraction layer for all controller haptic feedback and force feedback effects. Gameplay systems trigger haptics by GameplayTag (e.g., `Haptic.Damage.Heavy`) rather than calling raw UE5 haptic APIs. This component handles platform detection (Xbox rumble vs PS5 DualSense adaptive triggers vs generic PC gamepad), respects accessibility settings (`BPC_AccessibilitySettings.bHapticsEnabled`), and manages effect priority/conflict resolution.
## Dependencies
- **Requires:** [`DA_HapticProfile`](docs/blueprints/14-data-assets/121_DA_HapticProfile.md) (effect definitions), [`BPC_AccessibilitySettings`](docs/blueprints/12-settings/104_BPC_AccessibilitySettings.md) (master toggle), [`BPC_StateManager`](docs/blueprints/16-state/130_BPC_StateManager.md) (heart rate for heartbeat haptic)
- **Required By:** `BPC_HealthSystem` (damage haptics), `BPC_FirearmSystem` (weapon fire kick), `BPC_MeleeSystem` (melee impact), `BPC_PhysicsDragSystem` (grab/release), `BPC_ScareEventSystem` (tension rumble), `BP_ItemPickup` (pickup pulse), `BPC_MovementStateSystem` (footstep rumble via GASP notifies)
- **Engine/Plugin Requirements:** Enhanced Input Plugin (controller detection), PlayStation 5 Controller Plugin (DualSense adaptive triggers — optional, graceful fallback)
## Class Info
| Property | Value |
|----------|-------|
| **Parent Class** | `ActorComponent` |
| **Class Type** | Blueprint Component |
| **Asset Path** | `Content/Framework/Settings/BPC_HapticsController` |
| **Implements Interfaces** | None |
| **Attachment** | Player Controller (`PC_CoreController`) |
---
## 1. Enums
### `EHapticEvent`
| Value | Description |
|-------|-------------|
| `None = 0` | No haptic effect |
| `Damage = 1` | Player takes damage (intensity scales with amount) |
| `HeavyDamage = 2` | Critical/major damage hit |
| `Heartbeat = 3` | Heartbeat pulse (tempo from BPC_StateManager heart rate) |
| `WeaponFire = 4` | Weapon fire kick |
| `WeaponReload = 5` | Reload action feedback |
| `MeleeImpact = 6` | Melee weapon hit/kill |
| `Footstep = 7` | Footstep surface-dependent rumble |
| `Explosion = 8` | Nearby explosion or environmental blast |
| `PickupItem = 9` | Item picked up |
| `DropItem = 10` | Item dropped/discarded |
| `GrabObject = 11` | Physics object grabbed |
| `ReleaseObject = 12` | Physics object released/thrown |
| `ScareEvent = 13` | Jump scare / tension event |
| `AmbientPulse = 14` | Low-level ambient tension rumble |
| `UI_Confirm = 15` | Menu confirm/select haptic click |
| `UI_Navigate = 16` | Menu navigation tick |
| `LowHealth = 17` | Health-critical warning pulse |
| `StaminaExhausted = 18` | Stamina depleted heavy pulse |
| `Death = 19` | Player death rumble |
### `EHapticMotor`
| Value | Description |
|-------|-------------|
| `Left = 0` | Left (low-frequency) motor only |
| `Right = 1` | Right (high-frequency) motor only |
| `Both = 2` | Both motors simultaneously |
### `EControllerPlatform`
| Value | Description |
|-------|-------------|
| `Unknown = 0` | Platform not yet detected |
| `PC_Generic = 1` | PC with generic gamepad (XInput) |
| `Xbox = 2` | Xbox Series X\|S / Xbox One controller |
| `PS5_DualSense = 3` | PlayStation 5 DualSense controller |
| `PS4_DualShock = 4` | PlayStation 4 DualShock 4 controller |
---
## 2. Structs
### `S_HapticRequest`
| Field | Type | Description |
|-------|------|-------------|
| `ProfileTag` | `FGameplayTag` | Which DA_HapticProfile to use |
| `EventType` | `EHapticEvent` | Event category |
| `IntensityMultiplier` | `Float` | Scale intensity (0.02.0, 1.0 = default) |
| `DurationOverride` | `Float` | Override duration (-1 = use profile default) |
| `Priority` | `Int32` | Higher interrupts lower (0100) |
| `RequestTime` | `Float` | Game time when requested (for cooldown) |
---
## 3. Variables
### Configuration (Instance Editable)
| Variable | Type | Default | Category | Description |
|----------|------|---------|----------|-------------|
| `HapticProfileMap` | `TMap<FGameplayTag, DA_HapticProfile>` | `Empty` | `Config` | All haptic profiles loaded at startup |
| `bHapticsEnabled` | `Bool` | `true` | `Config` | Master toggle (synced from BPC_AccessibilitySettings) |
| `bEnableDualSenseTriggers` | `Bool` | `true` | `Config` | Enable adaptive trigger effects on PS5 |
| `bEnableSpeakerAudio` | `Bool` | `true` | `Config` | Enable controller speaker audio on PS5 |
| `MinTimeBetweenEffects` | `Float` | `0.05` | `Config` | Minimum seconds between any two effects (prevents rumble spam) |
| `HapticIntensityScale` | `Float` | `1.0` | `Config` | Global intensity multiplier (0.0 = off, 1.0 = full) |
| `HeartbeatMinBPM` | `Float` | `40.0` | `Config` | Minimum BPM for heartbeat haptic |
| `HeartbeatMaxBPM` | `Float` | `180.0` | `Config` | Maximum BPM for heartbeat haptic |
### Internal (Private)
| Variable | Type | Default | Category | Description |
|----------|------|---------|----------|-------------|
| `CurrentPlatform` | `EControllerPlatform` | `Unknown` | `State` | Detected controller platform |
| `bIsInitialized` | `Bool` | `false` | `State` | Whether Initialize has completed |
| `CachedPlayerController` | `APlayerController` | `None` | `Cache` | Cached owner PlayerController reference |
| `ActiveHapticEffect` | `UForceFeedbackEffect` | `None` | `State` | Currently playing effect asset |
| `LastPlayTime` | `Float` | `0.0` | `State` | Game time of last played effect |
| `PendingRequest` | `S_HapticRequest` | `Empty` | `State` | Currently queued request |
| `bHeartbeatActive` | `Bool` | `false` | `State` | Whether heartbeat loop is active |
---
## 4. Functions
### Public Functions
#### `Initialize` → `void`
- **Description:** Detects controller platform, loads all `DA_HapticProfile` instances into `HapticProfileMap`, caches PlayerController.
- **Parameters:** None
- **Blueprint Authority:** Local Client Only
- **Flow:**
1. Get Owner → Cast to `APlayerController` → Store as `CachedPlayerController`
2. Detect platform: Check `UGameplayStatics::GetPlatformName()` + connected input devices
3. Set `CurrentPlatform` (Xbox, PS5_DualSense, PS4_DualShock, or PC_Generic)
4. Load all `DA_HapticProfile` assets from `Content/Framework/DataAssets/Haptics/` into `HapticProfileMap`
5. If `BPC_AccessibilitySettings` exists on owner → bind to `OnHapticsToggleChanged`
6. Bind to `BPC_StateManager.OnHeartRateChanged` for heartbeat haptic
7. Set `bIsInitialized = true`
8. Broadcast `OnHapticsControllerInitialized`
#### `PlayHapticByTag` → `void`
- **Description:** Play a haptic effect by its GameplayTag. Main entry point for all gameplay systems.
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `ProfileTag` | `FGameplayTag` | Tag matching a DA_HapticProfile |
| `IntensityMultiplier` | `Float` | Scale intensity (default 1.0) |
- **Blueprint Authority:** Local Client Only
- **Flow:**
1. Validate `bHapticsEnabled` and `bIsInitialized` — if disabled, return
2. Check `MinTimeBetweenEffects` cooldown — if too soon, queue or skip
3. Look up `DA_HapticProfile` from `HapticProfileMap` by `ProfileTag`
4. If not found: log warning, return
5. Build `S_HapticRequest` with profile data
6. Call `PlayHapticInternal()` with the request
7. Broadcast `OnHapticPlayed` with tag
#### `PlayHapticByEvent` → `void`
- **Description:** Play a haptic effect by event type. Convenience wrapper for systems that don't know the exact tag.
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `EventType` | `EHapticEvent` | Which event to trigger |
| `IntensityMultiplier` | `Float` | Scale intensity (default 1.0) |
- **Flow:** Converts `EHapticEvent` to default tag (e.g., `Damage``Haptic.Damage.Default`) and calls `PlayHapticByTag`.
#### `StopHaptic` → `void`
- **Description:** Stop all currently playing haptic effects.
- **Flow:**
1. If `CachedPlayerController` valid: call `Stop Force Feedback` node
2. Set `ActiveHapticEffect = None`
3. If `bHeartbeatActive`: call `StopHeartbeatHaptic()`
4. Broadcast `OnHapticStopped`
#### `PlayHeartbeatHaptic` → `void`
- **Description:** Start or update the continuous heartbeat pulse haptic at the given BPM.
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `BeatsPerMinute` | `Float` | Heart rate in BPM (from BPC_StateManager) |
- **Flow:**
1. Clamp BPM to `HeartbeatMinBPM``HeartbeatMaxBPM`
2. Calculate pulse interval: `Interval = 60.0 / BPM`
3. Set `bHeartbeatActive = true`
4. Start a looping timer at `Interval` seconds
5. Each tick: call `PlayHapticByTag(Haptic.Heartbeat)` with intensity from BPM
6. If BPM changes: update timer interval
#### `StopHeartbeatHaptic` → `void`
- **Description:** Stop the continuous heartbeat haptic loop.
- **Flow:** Clear heartbeat timer, set `bHeartbeatActive = false`.
#### `SetHapticsEnabled` → `void`
- **Description:** Enable or disable all haptic output. Called by accessibility toggle.
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `bEnabled` | `Bool` | New state |
- **Flow:**
1. Set `bHapticsEnabled = bEnabled`
2. If disabled: call `StopHaptic()`
3. Broadcast `OnHapticsEnabledChanged`
#### `SetControllerPlatform` → `void`
- **Description:** Force a specific controller platform (for testing or hot-swap).
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `Platform` | `EControllerPlatform` | Target platform |
#### `GetControllerPlatform` → `EControllerPlatform`
- **Description:** Returns the detected controller platform. Read-only.
#### `IsDualSenseConnected` → `Bool`
- **Description:** Returns true if a PS5 DualSense controller is detected.
- **Flow:** Returns `CurrentPlatform == PS5_DualSense`
#### `SetDualSenseTriggerEffect` → `void`
- **Description:** Set adaptive trigger resistance on a specific DualSense trigger. No-op on non-DualSense platforms.
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `TriggerSide` | `Name` | "Left" or "Right" |
| `EffectType` | `Name` | "Resistance", "Vibration", "WeaponFire", "BowDraw", etc. |
| `StartPosition` | `Int32` | Trigger position where effect begins (09) |
| `Strength` | `Int32` | Effect strength (08) |
- **Blueprint Authority:** Local Client Only
- **Flow:**
1. If `CurrentPlatform != PS5_DualSense`: return (graceful no-op)
2. If `bEnableDualSenseTriggers == false`: return
3. Call platform-specific DualSense trigger API via `PlayerController`
4. Log effect for debugging
### Protected / Private Functions
#### `PlayHapticInternal` → `void`
- **Description:** Core haptic playback logic. Resolves platform, selects the right effect asset, handles priority conflicts.
- **Parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `Request` | `S_HapticRequest` | Full haptic request |
- **Flow:**
1. Check priority: if `Request.Priority < PendingRequest.Priority` and `PendingRequest` is active → skip
2. If `Request.Priority >= PendingRequest.Priority`: interrupt current effect
3. Resolve platform: select `UForceFeedbackEffect` from `DA_HapticProfile` for `CurrentPlatform`
4. If no platform-specific asset: fall back to generic PC effect
5. Apply `IntensityMultiplier` to `HapticIntensityScale`
6. Call `Play Force Feedback` node on `CachedPlayerController`:
- Input: `ForceFeedbackEffect`, `IntensityMultiplier`, `bLooping=false`, `bIgnoreTimeDilation=true`
7. Store as `ActiveHapticEffect`
8. Set `LastPlayTime = GameTimeInSeconds`
9. Broadcast `OnHapticPlayed`
#### `DetectControllerPlatform` → `void`
- **Description:** Queries the input system to determine which controller is connected.
- **Flow:**
1. Check `UGameplayStatics::GetPlatformName()`
2. If platform contains "PS5" → `PS5_DualSense`
3. If platform contains "PS4" → `PS4_DualShock`
4. If platform contains "Xbox" or "XboxOne" or "XSX" → `Xbox`
5. Otherwise: check connected devices → if gamepad found → `PC_Generic`, else → `Unknown`
---
## 5. Event Dispatchers
| Dispatcher | Parameters | Bind Access | Description |
|------------|-----------|-------------|-------------|
| `OnHapticsControllerInitialized` | — | `Public` | Fired after Initialize completes |
| `OnHapticPlayed` | `FGameplayTag ProfileTag`, `EHapticEvent EventType`, `Float Intensity` | `Public` | Fired when any haptic effect starts |
| `OnHapticStopped` | — | `Public` | Fired when haptics stop (manual or effect ends) |
| `OnHapticsEnabledChanged` | `Bool bEnabled` | `Public` | Fired when master toggle changes |
| `OnControllerPlatformChanged` | `EControllerPlatform NewPlatform` | `Public` | Fired on controller hot-swap |
| `OnDualSenseTriggerActivated` | `Name TriggerSide`, `Name EffectType` | `Public` | Fired when adaptive trigger effect applied (PS5 only) |
---
## 6. Overridden Events / Custom Events
### Event: `BeginPlay`
- **Description:** Startup. Calls `Initialize()`.
- **Flow:**
1. Call `Initialize()`
2. If `CachedPlayerController` is null: log error, return
3. Register for controller connection/disconnection events
### Event: `EndPlay`
- **Description:** Cleanup on component destruction.
- **Flow:**
1. Call `StopHaptic()` (stop all effects)
2. Call `StopHeartbeatHaptic()`
3. Unbind from all dispatchers
4. Clear cached references
---
## 7. Blueprint Graph Logic Flow
```mermaid
flowchart TD
A[BeginPlay] --> B[Initialize]
B --> C{Detect Controller Platform}
C --> D[Set CurrentPlatform]
D --> E[Load DA_HapticProfile Map]
E --> F[Bind to AccessibilitySettings.OnHapticsToggleChanged]
F --> G[Bind to StateManager.OnHeartRateChanged]
G --> H[Broadcast OnInitialized]
I[PlayHapticByTag] --> J{bHapticsEnabled?}
J -->|No| K[Return]
J -->|Yes| L{Cooldown check}
L -->|TooSoon| M[Queue or skip]
L -->|OK| N[Lookup DA_HapticProfile]
N --> O{Found?}
O -->|No| P[Log Warning]
O -->|Yes| Q[Build S_HapticRequest]
Q --> R[PlayHapticInternal]
R --> S{Platform == PS5?}
S -->|Yes| T[Play Force Feedback PS5 Profile]
S -->|No| U[Play Force Feedback Generic]
T --> V[Broadcast OnHapticPlayed]
U --> V
W[PlayHeartbeatHaptic] --> X[Clamp BPM]
X --> Y[Calculate Interval = 60/BPM]
Y --> Z[Start Looping Timer]
Z --> AA[Each Tick: PlayHapticByTag Haptic.Heartbeat]
```
---
## 8. Communication Matrix
| Who Talks | How | What Is Sent |
|-----------|-----|-------------|
| `BPC_HealthSystem` | `Function Call` | `BPC_HapticsController::PlayHapticByTag(Haptic.Damage.Heavy, Intensity)` on damage taken |
| `BPC_FirearmSystem` | `Function Call` | `BPC_HapticsController::PlayHapticByTag(Haptic.WeaponFire.Pistol)` on fire |
| `BPC_MeleeSystem` | `Function Call` | `BPC_HapticsController::PlayHapticByTag(Haptic.MeleeImpact)` on hit |
| `BPC_PhysicsDragSystem` | `Function Call` | `BPC_HapticsController::PlayHapticByTag(Haptic.Grab)` / `Haptic.Release` |
| `BPC_ScareEventSystem` | `Function Call` | `BPC_HapticsController::PlayHapticByTag(Haptic.ScareEvent)` on scare trigger |
| `BPC_ReloadSystem` | `Function Call` | `BPC_HapticsController::PlayHapticByTag(Haptic.WeaponReload)` on reload complete |
| `BP_ItemPickup` | `Function Call` | `BPC_HapticsController::PlayHapticByTag(Haptic.PickupItem)` on collect |
| `BPC_MovementStateSystem` | `Function Call` | `BPC_HapticsController::PlayHapticByTag(Haptic.Footstep. + SurfaceTag)` via GASP notify |
| `BPC_StateManager` | `Dispatcher` | `OnHeartRateChanged(BPM)``BPC_HapticsController::PlayHeartbeatHaptic(BPM)` |
| `BPC_AccessibilitySettings` | `Dispatcher` | `OnHapticsToggleChanged(bEnabled)``BPC_HapticsController::SetHapticsEnabled()` |
| `BPC_DeathHandlingSystem` | `Function Call` | `BPC_HapticsController::PlayHapticByTag(Haptic.Death)` on death |
| `BPC_DamageReceptionSystem` | `Function Call` | `BPC_HapticsController::PlayHapticByTag(Haptic.Explosion)` on area damage |
| `BPC_StaminaSystem` | `Function Call` | `BPC_HapticsController::PlayHapticByTag(Haptic.StaminaExhausted)` on empty |
| `BPC_HealthSystem` (low) | `Function Call` | `BPC_HapticsController::PlayHapticByTag(Haptic.LowHealth)` when <25% HP |
---
## 9. Validation / Testing Checklist
- [ ] `Initialize` correctly detects Xbox controller: `CurrentPlatform = Xbox`
- [ ] `Initialize` correctly detects PS5 DualSense: `CurrentPlatform = PS5_DualSense`
- [ ] `PlayHapticByTag` with valid tag plays force feedback on controller
- [ ] `PlayHapticByTag` with invalid tag logs warning but does not crash
- [ ] `StopHaptic` immediately stops all controller vibration
- [ ] `bHapticsEnabled = false` causes all `PlayHapticByTag` calls to be skipped
- [ ] Heartbeat haptic loops at correct interval (60 BPM 1 pulse/second)
- [ ] Heartbeat BPM changes dynamically when `PlayHeartbeatHaptic(120)` called
- [ ] DualSense trigger resistance activates on PS5 (R2 stiffens when aiming)
- [ ] Non-DualSense platforms gracefully skip trigger effects (no crash, no log spam)
- [ ] Priority conflict: higher priority effect interrupts lower (damage overrides footstep)
- [ ] `MinTimeBetweenEffects` prevents rapid-fire rumble spam
- [ ] Edge case: `PlayHapticByTag` before `Initialize` logs warning and returns
- [ ] Edge case: Controller disconnected mid-effect `StopHaptic` called safely
- [ ] Edge case: Multiple rapid `PlayHapticByTag` calls only highest priority plays
- [ ] Edge case: Platform hot-swap (Xbox PS5) fires `OnControllerPlatformChanged`
---
## 10. Reuse Notes
- Attach this component to the **Player Controller** (not the Pawn). Haptics are per-controller, per-player.
- All gameplay systems trigger haptics via GameplayTag never hardcode `Play Force Feedback` nodes.
- The `DA_HapticProfile` Data Asset stores platform-specific `UForceFeedbackEffect` curves. Designers author these in the Content Browser without touching Blueprints.
- For multiplayer: haptics are **local client only** never replicated. The server doesn't need to know about controller vibration.
- Heartbeat haptic is the only continuous/looping effect. All others are one-shot.
- DualSense adaptive triggers require the **PS5 Controller Plugin** enabled. Framework gracefully degrades on other platforms.
- The `Haptic.` GameplayTag namespace is documented in `DT_Tags_Player.csv` and `DA_GameTagRegistry`.
- Accessibility: `bHapticsEnabled` syncs with `BPC_AccessibilitySettings` so players can disable all vibration from settings.
- For platform profiles: create one `DA_HapticProfile` instance per event per platform, then reference all three in the profile map. `BPC_HapticsController` selects the right one at runtime.
---
## 11. Manual Implementation Guide
> **This section is for a human implementer building the Blueprint manually in UE5.**
> Follow these steps in order. Each function is broken down into specific UE5 Blueprint nodes.
### 11.1 Class Setup
1. Create a new Blueprint Class:
- Parent Class: `ActorComponent`
- Name: `BPC_HapticsController`
- Path: `Content/Framework/Settings/`
2. Add all variables from Section 3 to the Class Defaults.
- Configuration variables: set `Instance Editable`
- Internal variables: set to `Private` (no expose)
3. Add the Event Dispatchers from Section 5.
### 11.2 Variable Initialization (BeginPlay)
```
Event BeginPlay
├─ Get Owner → Cast to PlayerController → Store as CachedPlayerController
│ └─ If NOT valid: Print String "BPC_HapticsController: Owner is not a PlayerController!" → Return
├─ Call DetectControllerPlatform → Set CurrentPlatform
├─ Load Asset Registry → Get All Assets of Class (DA_HapticProfile)
│ └─ ForEach: Add to HapticProfileMap [ProfileTag → Asset]
├─ Get Owner → Get Component by Class (BPC_AccessibilitySettings)
│ └─ If valid: Bind Event OnHapticsToggleChanged → SetHapticsEnabled
│ └─ If valid: Read initial bHapticsEnabled value
├─ Get Owner Pawn → Get Component by Class (BPC_StateManager)
│ └─ If valid: Bind Event OnHeartRateChanged → PlayHeartbeatHaptic
├─ Set bIsInitialized = true
└─ Call OnHapticsControllerInitialized
```
### 11.3 Function Implementations
#### `PlayHapticByTag`
**Input Pins:** `ProfileTag` (GameplayTag), `IntensityMultiplier` (Float)
**Output Pins:** None
**Node-by-Node Logic:**
```
[Function: PlayHapticByTag]
Step 1: Branch on bHapticsEnabled → False: Return
Step 2: Get Game Time in Seconds → Subtract LastPlayTime → Compare to MinTimeBetweenEffects
Branch → Too soon: Return (or queue if needed)
Step 3: HapticProfileMap → Find (ProfileTag) → Store as FoundProfile
Step 4: Branch on FoundProfile valid?
True →
Step 4a: Make S_HapticRequest:
- ProfileTag = ProfileTag
- EventType = FoundProfile.EventType
- IntensityMultiplier = IntensityMultiplier
- DurationOverride = -1 (use profile default)
- Priority = FoundProfile.Priority
Step 4b: Call PlayHapticInternal(S_HapticRequest)
Step 4c: Call OnHapticPlayed(ProfileTag, FoundProfile.EventType, IntensityMultiplier)
False →
Step 4d: Print String Warning: "No haptic profile found for tag: {ProfileTag}"
```
**Nodes Used:** `Branch`, `FindGameplayTag`, `Map Find`, `Make Struct (S_HapticRequest)`, `Call Function`, `Print String`
#### `PlayHapticInternal`
**Input Pins:** `Request` (S_HapticRequest)
**Output Pins:** None
**Node-by-Node Logic:**
```
[Function: PlayHapticInternal]
Step 1: If ActiveHapticEffect is valid → Call StopHaptic (interrupt current)
Step 2: Break S_HapticRequest → get ProfileTag
Step 3: Look up DA_HapticProfile from HapticProfileMap
Step 4: Switch on CurrentPlatform:
Case PS5_DualSense: Get PS5_ForceFeedbackCurve from profile
Case Xbox: Get Xbox_ForceFeedbackCurve from profile
Default: Get Generic_ForceFeedbackCurve from profile
Step 5: Branch on selected curve valid?
True →
Step 5a: CachedPlayerController → Play Force Feedback
- Force Feedback Effect: selected curve asset
- Intensity Multiplier: Request.IntensityMultiplier * HapticIntensityScale
- bLooping: false
- bIgnore Time Dilation: true
Step 5b: Set ActiveHapticEffect = selected curve
False →
Step 5c: Print String Warning: "No ForceFeedbackEffect for platform {CurrentPlatform}"
Step 6: Set LastPlayTime = Get Game Time in Seconds
```
**Nodes Used:** `Switch on EControllerPlatform`, `Break S_HapticRequest`, `Map Find`, `Play Force Feedback (PlayerController)`, `Branch`
#### `PlayHeartbeatHaptic`
**Input Pins:** `BeatsPerMinute` (Float)
**Output Pins:** None
**Node-by-Node Logic:**
```
[Function: PlayHeartbeatHaptic]
Step 1: Clamp (BeatsPerMinute, HeartbeatMinBPM, HeartbeatMaxBPM) → Store as ClampedBPM
Step 2: Divide 60.0 / ClampedBPM → Store as PulseInterval
Step 3: If bHeartbeatActive:
True → Clear Timer by Handle (HeartbeatTimerHandle)
Step 4: Set bHeartbeatActive = true
Step 5: Set Timer by Event:
- Event: Custom Event (HeartbeatPulse)
- Time: PulseInterval
- Looping: true
- Store handle as HeartbeatTimerHandle
[Custom Event: HeartbeatPulse]
→ Call PlayHapticByTag(Haptic.Heartbeat, 1.0)
```
**Nodes Used:** `Clamp (float)`, `Divide`, `Set Timer by Event`, `Clear Timer by Handle`
#### `SetHapticsEnabled`
**Input Pins:** `bEnabled` (Bool)
**Output Pins:** None
```
[Function: SetHapticsEnabled]
Step 1: Set bHapticsEnabled = bEnabled
Step 2: Branch:
False → Call StopHaptic
Step 3: Call OnHapticsEnabledChanged(bEnabled)
```
#### `DetectControllerPlatform`
```
[Function: DetectControllerPlatform]
Step 1: Get Platform Name → Store as PlatformStr
Step 2: String Contains (PlatformStr, "PS5") → True: Set CurrentPlatform = PS5_DualSense → Return
Step 3: String Contains (PlatformStr, "PS4") → True: Set CurrentPlatform = PS4_DualShock → Return
Step 4: String Contains (PlatformStr, "Xbox") → True: Set CurrentPlatform = Xbox → Return
Step 5: Get Input Device Type → Switch on Type:
Gamepad → Set CurrentPlatform = PC_Generic
Default → Set CurrentPlatform = Unknown
```
**Nodes Used:** `Get Platform Name`, `Contains (string)`, `Switch on String`, `Get Input Device Type`
### 11.4 Event Dispatcher Bindings (Inbound Listeners)
| Bind to Dispatcher | Custom Event to Create | What it Does |
|--------------------------------------|------------------------|--------------|
| `BPC_StateManager.OnHeartRateChanged` | `OnHeartRateChanged_Handler` | `PlayHeartbeatHaptic(CurrentHeartRate)` |
| `BPC_AccessibilitySettings.OnHapticsToggleChanged` | `OnHapticsToggle_Handler` | `SetHapticsEnabled(bEnabled)` |
### 11.5 Multiplayer Networking
- This component is **local client only**. No replication needed.
- Haptics play only on the local player's controller.
- No `HasAuthority()` gates needed haptics are cosmetic.
- For listen server hosts: `IsLocalPlayerController()` check before playing effects.
### 11.6 Quick Node Reference
| Node | Where to Find | Used For |
|------|---------------|----------|
| `Play Force Feedback` | Right-click "Play Force Feedback" | Playing rumble effect on controller |
| `Stop Force Feedback` | Right-click "Stop Force Feedback" | Stopping all active vibration |
| `Set Timer by Event` | Right-click "Set Timer by Event" | Looping heartbeat pulse |
| `Get Platform Name` | Right-click "Get Platform Name" | Detecting Xbox/PS5/PC |
| `Clamp (float)` | Right-click "Clamp" | Clamping BPM range |
| `Make Struct` | Right-click "Make S_HapticRequest" | Building haptic request |
| `Map Find` | Right-click "Find" | Looking up profile by tag |
| `Get Game Time in Seconds` | Right-click "Get Game Time" | Cooldown tracking |
---
## 12. Blueprint Build Checklist
- [ ] Create Blueprint class: `BPC_HapticsController` (parent: `ActorComponent`)
- [ ] Add all variables from Section 3 with correct types and defaults
- [ ] Create `EHapticEvent`, `EHapticMotor`, `EControllerPlatform` enums (in Content Browser)
- [ ] Create `S_HapticRequest` struct (in Content Browser)
- [ ] Build `BeginPlay` event `Initialize` chain
- [ ] Implement `DetectControllerPlatform` function
- [ ] Implement `PlayHapticInternal` with Platform Switch
- [ ] Implement `PlayHapticByTag` (public entry point)
- [ ] Implement `PlayHapticByEvent` (convenience wrapper)
- [ ] Implement `StopHaptic` / `StopHeartbeatHaptic`
- [ ] Implement `PlayHeartbeatHaptic` with looping timer
- [ ] Implement `SetHapticsEnabled` / `SetDualSenseTriggerEffect`
- [ ] Create all 6 event dispatchers
- [ ] Bind to `BPC_StateManager.OnHeartRateChanged`
- [ ] Bind to `BPC_AccessibilitySettings.OnHapticsToggleChanged`
- [ ] Create at least one `DA_HapticProfile` instance for `Haptic.Damage.Default`
- [ ] Test: PlayHapticByTag triggers vibration on Xbox controller
- [ ] Test: PlayHapticByTag triggers vibration on PS5 DualSense
- [ ] Test: bHapticsEnabled=false blocks all effects
- [ ] Test: Heartbeat BPM changes pitch/speed of pulse
- [ ] Test: Rapid-fire calls respect MinTimeBetweenEffects
---
*Blueprint Spec: Haptics Controller. Conforms to TEMPLATE.md v2.0 — part of the UE5 Modular Game Framework, SETTINGS layer.*

View File

@@ -51,7 +51,7 @@ The Modular Game Framework uses `UDataAsset` (and subclass `UPrimaryDataAsset` w
| 11 | `DA_ScareEvent` | Adaptive | `BPC_ScareEventSystem` | | 11 | `DA_ScareEvent` | Adaptive | `BPC_ScareEventSystem` |
| 12 | `DA_RoomMutation` | Adaptive | `BPC_AdaptiveEnvironmentDirector` | | 12 | `DA_RoomMutation` | Adaptive | `BPC_AdaptiveEnvironmentDirector` |
| 13 | `DA_BehaviourVariant` | AI | `BPC_BehaviourVariantSelector` | | 13 | `DA_BehaviourVariant` | AI | `BPC_BehaviourVariantSelector` |
| 14 | `DA_HapticProfile` | Settings | `BPC_HapticsController` | | 14 | `DA_HapticProfile` | Settings | `BPC_HapticsController` (148) |
| 15 | `DA_AdaptationRule` | Adaptive | `BPC_DifficultyManager` | | 15 | `DA_AdaptationRule` | Adaptive | `BPC_DifficultyManager` |
| 16 | `DA_EquipmentConfig` | Inventory | `BPC_EquipmentSlotSystem` | | 16 | `DA_EquipmentConfig` | Inventory | `BPC_EquipmentSlotSystem` |
| 17 | `DA_PuzzleData` | Interaction | `BP_PuzzleDeviceActor` | | 17 | `DA_PuzzleData` | Interaction | `BP_PuzzleDeviceActor` |

View File

@@ -1,52 +1,178 @@
# DA_HapticProfile — Data Asset # DA_HapticProfile — Data Asset
**Parent Class:** `UDataAsset` **Parent Class:** `UPrimaryDataAsset`
**Dependencies:** [`BPC_HapticsController`](../12-settings/) **Dependencies:** [`BPC_HapticsController`](../12-settings/148_BPC_HapticsController.md)
**Purpose:** Defines haptic feedback profiles for controller vibration and force feedback effects tied to gameplay events. **Purpose:** Defines haptic feedback profiles for controller vibration and force feedback effects tied to gameplay events. Each profile maps a GameplayTag to a platform-specific `UForceFeedbackEffect` waveform asset with configurable intensity, duration, motor mask, and priority.
---
## Enums Used
| Enum | Defined In | Values |
|------|-----------|--------|
| `EHapticEvent` | `BPC_HapticsController` (148) | Damage, HeavyDamage, Heartbeat, WeaponFire, WeaponReload, MeleeImpact, Footstep, Explosion, PickupItem, DropItem, GrabObject, ReleaseObject, ScareEvent, AmbientPulse, UI_Confirm, UI_Navigate, LowHealth, StaminaExhausted, Death |
| `EHapticMotor` | `BPC_HapticsController` (148) | Left, Right, Both |
--- ---
## Variables / Structure ## Variables / Structure
### Core Fields
| Field | Type | Description | | Field | Type | Description |
|-------|------|-------------| |-------|------|-------------|
| `ProfileTag` | `FGameplayTag` | Unique haptic profile identifier | | `ProfileTag` | `FGameplayTag` | Unique haptic profile identifier (e.g., `Haptic.Damage.Heavy`) |
| `EventType` | `EHapticEvent` | Damage, Heartbeat, Explosion, Footstep, WeaponFire, AmbientPulse | | `EventType` | `EHapticEvent` | Event category this profile handles |
| `IntensityCurve` | `UCurveFloat*` | Vibration intensity over time | | `ForceFeedbackEffect_Generic` | `UForceFeedbackEffect*` | Waveform curve asset for PC/Xbox generic controllers |
| `Duration` | `float` | Total haptic effect duration (seconds) | | `ForceFeedbackEffect_PS5` | `UForceFeedbackEffect*` | Waveform curve asset for PS5 DualSense (higher fidelity) |
| `MotorMask` | `EHapticMotor` | Left, Right, Both | | `ForceFeedbackEffect_Xbox` | `UForceFeedbackEffect*` | Waveform curve asset for Xbox controllers (optional override) |
| `Priority` | `int32` | Higher priority overrides lower during conflicts | | `IntensityCurve` | `UCurveFloat*` | Optional: vibration intensity over time (0.01.0) |
| `bCanInterrupt` | `bool` | Can this effect be interrupted by higher priority | | `Duration` | `float` | Total haptic effect duration in seconds |
| `PlatformMinIntensity` | `float` | Minimum intensity per platform capability | | `MotorMask` | `EHapticMotor` | Left motor, Right motor, or Both |
| `Priority` | `int32` | Higher priority overrides lower during conflicts (0 = lowest, 100 = highest) |
| `bCanInterrupt` | `bool` | Can this effect be interrupted by a higher-priority effect? |
| `bIgnoreTimeDilation` | `bool` | Should this play at real-time regardless of game speed? |
### Platform-Specific Fields
| Field | Type | Description |
|-------|------|-------------|
| `PlatformMinIntensity` | `float` | Minimum intensity threshold before effect is felt (0.01.0) |
| `DualSense_TriggerSide` | `FName` | PS5 adaptive trigger side ("Left", "Right", "None") |
| `DualSense_TriggerEffect` | `FName` | PS5 trigger effect type ("Resistance", "Vibration", "WeaponFire", "BowDraw", "None") |
| `DualSense_TriggerStartPosition` | `int32` | PS5 trigger position where effect begins (09) |
| `DualSense_TriggerStrength` | `int32` | PS5 trigger effect strength (08) |
### Heartbeat-Specific Fields
| Field | Type | Description |
|-------|------|-------------|
| `bIsHeartbeatProfile` | `bool` | Whether this profile is used for heartbeat pulse effects |
| `TargetBPMRange_Min` | `float` | Minimum BPM this profile handles |
| `TargetBPMRange_Max` | `float` | Maximum BPM this profile handles |
--- ---
## Gameplay Tags ## Gameplay Tags
- Namespace: `Haptic.<Event>` (e.g., `Haptic.Damage.Heavy`, `Haptic.Heartbeat`)
- Namespace: `Haptic.<Category>.<Subcategory>`
- Examples: `Haptic.Damage.Heavy`, `Haptic.Heartbeat.Normal`, `Haptic.WeaponFire.Pistol`
- All tags must be registered in `DT_Tags_Player.csv` for validation at startup
### Full Tag Hierarchy
```
Haptic.
├── Damage.Light
├── Damage.Heavy
├── Damage.Critical
├── Heartbeat.Normal
├── Heartbeat.Fast
├── Heartbeat.Panic
├── WeaponFire.Pistol
├── WeaponFire.Shotgun
├── WeaponReload
├── MeleeImpact.Crowbar
├── MeleeImpact.Default
├── Footstep.Tile
├── Footstep.Wood
├── Footstep.Concrete
├── Footstep.Metal
├── Footstep.Gravel
├── Explosion
├── Pickup.Item
├── Pickup.Weapon
├── Grab
├── Release.Throw
├── Scare.JumpScare
├── Scare.TensionRise
├── Ambient.Void
├── Ambient.Default
├── LowHealth
├── StaminaExhausted
├── Death
├── UI.Confirm
└── UI.Navigate
```
--- ---
## Validation Rules ## Validation Rules
- `ProfileTag` must be unique
- `Duration` must be > 0 - `ProfileTag` must be unique across all profiles
- `IntensityCurve` must be valid - `Duration` must be > 0.0
- At least one `ForceFeedbackEffect_*` must be assigned (Generic falls back for all platforms)
- `Priority` must be 0100
- If `bIsHeartbeatProfile` is true, `TargetBPMRange_Min` and `TargetBPMRange_Max` must be set
- If `DualSense_TriggerSide != "None"`, trigger effect fields must be valid
--- ---
## Example Data ## Example Data
### Damage Profile — Heavy Hit
| Field | Value | | Field | Value |
|-------|-------| |-------|-------|
| ProfileTag | `Haptic.Damage.Critical` | | `ProfileTag` | `Haptic.Damage.Heavy` |
| EventType | Damage | | `EventType` | `HeavyDamage` |
| Duration | 0.5 | | `Duration` | `0.4` |
| MotorMask | Both | | `MotorMask` | `Both` |
| Priority | 10 | | `Priority` | `80` |
| `bCanInterrupt` | `true` |
| `bIgnoreTimeDilation` | `true` |
| `ForceFeedbackEffect_Generic` | `FFE_Damage_Heavy` |
| `ForceFeedbackEffect_PS5` | `FFE_Damage_Heavy_PS5` |
### Heartbeat Profile — Normal
| Field | Value |
|-------|-------|
| `ProfileTag` | `Haptic.Heartbeat.Normal` |
| `EventType` | `Heartbeat` |
| `Duration` | `0.1` |
| `MotorMask` | `Left` |
| `Priority` | `30` |
| `bCanInterrupt` | `true` |
| `bIsHeartbeatProfile` | `true` |
| `TargetBPMRange_Min` | `60.0` |
| `TargetBPMRange_Max` | `90.0` |
| `ForceFeedbackEffect_Generic` | `FFE_Heartbeat` |
### Weapon Fire — Pistol
| Field | Value |
|-------|-------|
| `ProfileTag` | `Haptic.WeaponFire.Pistol` |
| `EventType` | `WeaponFire` |
| `Duration` | `0.08` |
| `MotorMask` | `Right` |
| `Priority` | `50` |
| `DualSense_TriggerSide` | `Right` |
| `DualSense_TriggerEffect` | `WeaponFire` |
| `DualSense_TriggerStartPosition` | `4` |
| `DualSense_TriggerStrength` | `6` |
--- ---
## Consumed By ## Consumed By
- [`BPC_HapticsController`](../12-settings/) - [`BPC_HapticsController`](../12-settings/148_BPC_HapticsController.md) — loads all profiles into `HapticProfileMap` at initialization
## Referenced By
- `BPC_HealthSystem` (08) — damage haptics
- `BPC_FirearmSystem` (74) — weapon fire haptics
- `BPC_MeleeSystem` (76) — melee impact haptics
- `BPC_ScareEventSystem` (101) — scare event haptics
- `BPC_MovementStateSystem` (11) — footstep haptics
- `BPC_ReloadSystem` (78) — reload haptics
- `BPC_PhysicsDragSystem` (22) — grab/release haptics
- `BP_ItemPickup` (25) — pickup haptics
- `BPC_DeathHandlingSystem` (39) — death haptics
- `BPC_StaminaSystem` (09) — stamina exhausted haptics
---
## Reuse Notes ## Reuse Notes
- Haptic profiles are platform-agnostic; translation handled by PlatformServiceAbstraction - Haptic profiles are platform-agnostic in design; `BPC_HapticsController` selects the correct `UForceFeedbackEffect` asset at runtime based on `CurrentPlatform`.
- Designers create FFE waveform curves in the Content Browser — no Blueprint changes needed for tuning.
- For heartbeat profiles: create 3 instances (Normal 60-90 BPM, Fast 90-140 BPM, Panic 140-180 BPM). `BPC_HapticsController` auto-selects based on current BPM.
- For footstep profiles: create one per surface type. `BPC_MovementStateSystem` selects based on physical surface trace result.
- Priority guidelines: 020 (ambient/footsteps), 3050 (weapons/reload/pickups), 6080 (damage), 90100 (scares/death).
- The `Haptic.` namespace should be registered in `DT_Tags_Player.csv` so `DA_GameTagRegistry` validates them at startup.

View File

@@ -5,7 +5,7 @@ Primary Data Asset that defines all Enhanced Input bindings across three platfor
## Dependencies ## Dependencies
- **Requires:** None (self-contained Data Asset) - **Requires:** None (self-contained Data Asset)
- **Required By:** [`SS_EnhancedInputManager`](docs/blueprints/15-input/128_SS_EnhancedInputManager.md) (loads and caches this), [`SS_SettingsSystem`](docs/blueprints/12-settings/105_SS_SettingsSystem.md) (references for key rebinding) - **Required By:** [`SS_EnhancedInputManager`](docs/blueprints/15-input/128_SS_EnhancedInputManager.md) (loads and caches this), [`SS_SettingsSystem`](docs/blueprints/12-settings/105_SS_SettingsSystem.md) (references for key rebinding), [`BPC_HapticsController`](docs/blueprints/12-settings/148_BPC_HapticsController.md) (reads `bEnableControllerRumble` for haptics master toggle)
- **Engine/Plugin Requirements:** Enhanced Input Plugin, `UInputMappingContext`, `UInputAction` - **Engine/Plugin Requirements:** Enhanced Input Plugin, `UInputMappingContext`, `UInputAction`
## Class Info ## Class Info
@@ -68,7 +68,7 @@ Primary Data Asset that defines all Enhanced Input bindings across three platfor
| `PlatformProfiles` | `TArray<S_PlatformProfile>` | `Empty` | `Profiles` | One profile per platform (3 total) | | `PlatformProfiles` | `TArray<S_PlatformProfile>` | `Empty` | `Profiles` | One profile per platform (3 total) |
| `ContextDefinitions` | `TArray<S_ContextDefinition>` | `Empty` | `Contexts` | IMC asset references and defaults | | `ContextDefinitions` | `TArray<S_ContextDefinition>` | `Empty` | `Contexts` | IMC asset references and defaults |
| `GlobalDeadZone` | `Float` | `0.15` | `Global` | Fallback analog stick dead zone | | `GlobalDeadZone` | `Float` | `0.15` | `Global` | Fallback analog stick dead zone |
| `bEnableControllerRumble` | `Bool` | `true` | `Global` | Master toggle for force feedback | | `bEnableControllerRumble` | `Bool` | `true` | `Global` | Master toggle for force feedback (read by BPC_HapticsController) |
| `bSwapSticksForLeftHanded` | `Bool` | `false` | `Accessibility` | Swap left/right stick for accessibility | | `bSwapSticksForLeftHanded` | `Bool` | `false` | `Accessibility` | Swap left/right stick for accessibility |
| `AxisInvertSettings` | `TMap<FName, Bool>` | `Empty` | `Accessibility` | Per-action axis inversion (e.g., "IA_Look" = InvertY) | | `AxisInvertSettings` | `TMap<FName, Bool>` | `Empty` | `Accessibility` | Per-action axis inversion (e.g., "IA_Look" = InvertY) |

View File

@@ -289,6 +289,7 @@ flowchart TD
| `BPC_CutsceneBridge` | `Function Call` | `SS_EnhancedInputManager::PushContext/PopContext` | | `BPC_CutsceneBridge` | `Function Call` | `SS_EnhancedInputManager::PushContext/PopContext` |
| `BPC_HidingSystem` | `Function Call` | `SS_EnhancedInputManager::PushContext(Hiding) / PopContext(Hiding)` | | `BPC_HidingSystem` | `Function Call` | `SS_EnhancedInputManager::PushContext(Hiding) / PopContext(Hiding)` |
| `BPC_ActiveItemSystem` | `Function Call` | `SS_EnhancedInputManager::IsActionPressed("IA_QuickSlot1")` | | `BPC_ActiveItemSystem` | `Function Call` | `SS_EnhancedInputManager::IsActionPressed("IA_QuickSlot1")` |
| `BPC_HapticsController` (148) | `Function Call` | `SS_EnhancedInputManager::GetControllerPlatform()` — for platform detection |
| `WBP_PauseMenu` | `Function Call` | `SS_EnhancedInputManager::PushContext(UI) / PopContext(UI)` | | `WBP_PauseMenu` | `Function Call` | `SS_EnhancedInputManager::PushContext(UI) / PopContext(UI)` |
| `WBP_InventoryMenu` | `Function Call` | `SS_EnhancedInputManager::PushContext(WristwatchUI)` | | `WBP_InventoryMenu` | `Function Call` | `SS_EnhancedInputManager::PushContext(WristwatchUI)` |
| `BP_PuzzleDeviceActor` | `Function Call` | `SS_EnhancedInputManager::PushContext(Inspection)` | | `BP_PuzzleDeviceActor` | `Function Call` | `SS_EnhancedInputManager::PushContext(Inspection)` |

View File

@@ -114,7 +114,7 @@ These systems appear in the Plan but may need explicit spec files or are covered
- `BPC_EndingCompletionTracker` — Not yet created; Master Section 11.4 - `BPC_EndingCompletionTracker` — Not yet created; Master Section 11.4
- `BPC_MetaProgressionSystem` — Not yet created; Master Section 11.5 - `BPC_MetaProgressionSystem` — Not yet created; Master Section 11.5
- `BPC_RunSummarySystem` — Already exists at `05-saveload/43_BPC_RunHistoryTracker.md` (may need rename or separate file) - `BPC_RunSummarySystem` — Already exists at `05-saveload/43_BPC_RunHistoryTracker.md` (may need rename or separate file)
- `BPC_HapticsController`Not yet created; Master Section 12.2 - `BPC_HapticsController`✅ Created (system 148); `docs/blueprints/12-settings/148_BPC_HapticsController.md`
- `BPC_PlatformServiceAbstraction` — Not yet created; Master Section 12.3 - `BPC_PlatformServiceAbstraction` — Not yet created; Master Section 12.3
--- ---

View File

@@ -1,6 +1,6 @@
# Master Blueprint Index — UE5 Modular Game Framework # Master Blueprint Index — UE5 Modular Game Framework
**Version:** 3.4 | **Generated:** 2026-05-22 | **Total Files:** 147 numbered + 1 starter + 1 supplementary (149 total specs) | **C++:** 27 source files (15 full + 10 stubs + 2 utility) **Version:** 3.5 | **Generated:** 2026-05-22 | **Total Files:** 148 numbered + 1 starter + 1 supplementary (150 total specs) | **C++:** 27 source files (15 full + 10 stubs + 2 utility)
This document is the canonical index of every Blueprint specification file in the framework. Each entry links to its full spec document and includes: file name, asset type, parent class, purpose summary, and key dependencies. This document is the canonical index of every Blueprint specification file in the framework. Each entry links to its full spec document and includes: file name, asset type, parent class, purpose summary, and key dependencies.
@@ -49,7 +49,7 @@ docs/blueprints/
├── 09-ai/ ← AI, Perception & Encounters (9 files) ├── 09-ai/ ← AI, Perception & Encounters (9 files)
├── 10-adaptive/ ← Adaptive Environment, Atmosphere & Scare (15 files) ├── 10-adaptive/ ← Adaptive Environment, Atmosphere & Scare (15 files)
├── 11-meta/ ← Achievements, Progression & Meta (2 files) ├── 11-meta/ ← Achievements, Progression & Meta (2 files)
├── 12-settings/ ← Settings, Accessibility & Platform (2 files) ├── 12-settings/ ← Settings, Accessibility, Haptics & Platform (3 files)
├── 13-polish/ ← Tutorial, Loading, Credits, Debug (9 files) ├── 13-polish/ ← Tutorial, Loading, Credits, Debug (9 files)
├── 14-data-assets/ ← Data Asset definitions (16 files) ├── 14-data-assets/ ← Data Asset definitions (16 files)
├── 15-input/ ← Enhanced Input System (1 file) ├── 15-input/ ← Enhanced Input System (1 file)
@@ -181,6 +181,7 @@ docs/blueprints/
| — | — | — | — | — | — | | — | — | — | — | — | — |
| 104 | [`104_BPC_AccessibilitySettings`](12-settings/104_BPC_AccessibilitySettings.md) | `BPC_` Component | `ActorComponent` | Accessibility; subtitles, colorblind, controller remap, difficulty | 12-settings | | 104 | [`104_BPC_AccessibilitySettings`](12-settings/104_BPC_AccessibilitySettings.md) | `BPC_` Component | `ActorComponent` | Accessibility; subtitles, colorblind, controller remap, difficulty | 12-settings |
| 105 | [`105_SS_SettingsSystem`](12-settings/105_SS_SettingsSystem.md) | `SS_` Subsystem | `GameInstanceSubsystem` | Settings subsystem; persistent settings, apply, reset, platform | 12-settings | | 105 | [`105_SS_SettingsSystem`](12-settings/105_SS_SettingsSystem.md) | `SS_` Subsystem | `GameInstanceSubsystem` | Settings subsystem; persistent settings, apply, reset, platform | 12-settings |
| 148 | [`148_BPC_HapticsController`](12-settings/148_BPC_HapticsController.md) | `BPC_` Component | `ActorComponent` | Haptics controller; GameplayTag-driven force feedback, DualSense triggers, heartbeat pulse | 12-settings |
| — | — | — | — | — | — | | — | — | — | — | — | — |
| 106 | [`106_BPC_AnalyticsTracker`](13-polish/106_BPC_AnalyticsTracker.md) | `BPC_` Component | `ActorComponent` | Analytics; event tracking, session metrics, telemetry | 13-polish | | 106 | [`106_BPC_AnalyticsTracker`](13-polish/106_BPC_AnalyticsTracker.md) | `BPC_` Component | `ActorComponent` | Analytics; event tracking, session metrics, telemetry | 13-polish |
| 107 | [`107_BPC_DevCheatManager`](13-polish/107_BPC_DevCheatManager.md) | `BPC_` Component | `ActorComponent` | Developer cheats; god mode, noclip, give item, teleport | 13-polish | | 107 | [`107_BPC_DevCheatManager`](13-polish/107_BPC_DevCheatManager.md) | `BPC_` Component | `ActorComponent` | Developer cheats; god mode, noclip, give item, teleport | 13-polish |
@@ -234,7 +235,7 @@ docs/blueprints/
| Prefix | Type | Count | | Prefix | Type | Count |
|--------|------|-------| |--------|------|-------|
| `BPC_` | Blueprint Component | 80 | | `BPC_` | Blueprint Component | 81 |
| `BP_` | Blueprint Actor | 11 | | `BP_` | Blueprint Actor | 11 |
| `WBP_` | Widget Blueprint | 14 | | `WBP_` | Widget Blueprint | 14 |
| `DA_` | Data Asset | 19 | | `DA_` | Data Asset | 19 |
@@ -247,7 +248,7 @@ docs/blueprints/
| `AI_` | AI Controller | 1 | | `AI_` | AI Controller | 1 |
| `BB_` | Blackboard | 1 | | `BB_` | Blackboard | 1 |
| `E_` | Enum | 5 | | `E_` | Enum | 5 |
| **Total** | | **158** | | **Total** | | **159** |
--- ---
@@ -261,9 +262,10 @@ Below are the most cross-referenced systems — these are the ones the State Man
| `BPC_MovementStateSystem` (11) | StateManager, GASP AnimBP, Stamina, Camera, InteractionDetector, AudioAtmosphere | | `BPC_MovementStateSystem` (11) | StateManager, GASP AnimBP, Stamina, Camera, InteractionDetector, AudioAtmosphere |
| `BPC_HidingSystem` (12) | StateManager, AIPerception, StressSystem, CameraStateLayer | | `BPC_HidingSystem` (12) | StateManager, AIPerception, StressSystem, CameraStateLayer |
| `BPC_DeathHandlingSystem` (39) | HealthSystem, StateManager, AltDeathSpace, Corpse, Respawn, RunHistory, UIManager | | `BPC_DeathHandlingSystem` (39) | HealthSystem, StateManager, AltDeathSpace, Corpse, Respawn, RunHistory, UIManager |
| `SS_EnhancedInputManager` (128) | StateManager (context changes), all WBP_ menus, PlayerController | | `SS_EnhancedInputManager` (128) | StateManager (context changes), all WBP_ menus, PlayerController, BPC_HapticsController (controller platform detection) |
| `BPC_StateManager` (130) | EVERY system (central query point) | | `BPC_StateManager` (130) | EVERY system (central query point) |
| `SS_AudioManager` (132) | ALL systems that play audio, BP_RoomAudioZone, WBP_SettingsMenu, BPC_StateManager (heart rate → audio) | | `SS_AudioManager` (132) | ALL systems that play audio, BP_RoomAudioZone, WBP_SettingsMenu, BPC_StateManager (heart rate → audio) |
| `BPC_HapticsController` (148) | HealthSystem, FirearmSystem, MeleeSystem, ScareEventSystem, PhysicsDrag, MovementState, ReloadSystem, DeathHandling, Staminasystem, StateManager → heartbeat, AccessibilitySettings |
| `ABP_GASP` (external) | StateManager (overlay + action intensity), MovementState, Hiding, Stamina, Embodiment | | `ABP_GASP` (external) | StateManager (overlay + action intensity), MovementState, Hiding, Stamina, Embodiment |
--- ---
@@ -294,7 +296,7 @@ Below are the most cross-referenced systems — these are the ones the State Man
| `BPC_DifficultyManager` | 89 | `BPC_DocumentArchiveSystem` | 29 | `DA_EncounterData` | 119 | | `BPC_DifficultyManager` | 89 | `BPC_DocumentArchiveSystem` | 29 | `DA_EncounterData` | 119 |
| `BPC_EmbodimentSystem` | 13 | `BPC_EndingAccumulator` | 68 | `DA_EquipmentConfig` | 120 | | `BPC_EmbodimentSystem` | 13 | `BPC_EndingAccumulator` | 68 | `DA_EquipmentConfig` | 120 |
| `BPC_EquipmentSlotSystem` | 30 | `BPC_ErrorHandler` | 108 | `DA_HapticProfile` | 121 | | `BPC_EquipmentSlotSystem` | 30 | `BPC_ErrorHandler` | 108 | `DA_HapticProfile` | 121 |
| `BPC_FearSystem` | 90 | `BPC_FirearmSystem` | 74 | `DA_InputMappingProfile` | 129 | | `BPC_FearSystem` | 90 | `BPC_FirearmSystem` | 74 | `BPC_HapticsController` | 148 |
| `BPC_FPSCounter` | 109 | `BPC_HealthSystem` | 8 | `DA_InteractionData` | 122 | | `BPC_FPSCounter` | 109 | `BPC_HealthSystem` | 8 | `DA_InteractionData` | 122 |
| `BPC_HidingSystem` | 12 | `BPC_HitReactionSystem` | 75 | `DA_ItemData` | 7 | | `BPC_HidingSystem` | 12 | `BPC_HitReactionSystem` | 75 | `DA_ItemData` | 7 |
| `BPC_InteractionDetector` | 16 | `BPC_InventorySystem` | 31 | `DA_NarrativeDataAssets` | 66 | | `BPC_InteractionDetector` | 16 | `BPC_InventorySystem` | 31 | `DA_NarrativeDataAssets` | 66 |
@@ -326,7 +328,7 @@ Below are the most cross-referenced systems — these are the ones the State Man
--- ---
*Master Blueprint Index v3.4 — The single reference document for every file in the framework. Now 147 files with State Management, MetaSounds Audio, Multiplayer Networking, and Planar Capture System support.* *Master Blueprint Index v3.5 — The single reference document for every file in the framework. Now 148 files with State Management, MetaSounds Audio, Multiplayer Networking, Planar Capture System, and Haptics Controller support.*
--- ---

View File

@@ -181,6 +181,7 @@ Abbreviations:
| # | System | C++ | BP Spec | BP Asset | | # | System | C++ | BP Spec | BP Asset |
|---|--------|-----|---------|----------| |---|--------|-----|---------|----------|
| 102-114 | All 13 systems | 🔵 | ✅ | BP children + widget BPs | | 102-114 | All 13 systems | 🔵 | ✅ | BP children + widget BPs |
| 148 | `BPC_HapticsController` | 🔵 (Blueprint-only) | ✅ | ⬜ BP child attach to PlayerController |
### Data Assets (14-data-assets — 16 systems) ### Data Assets (14-data-assets — 16 systems)

View File

@@ -724,6 +724,7 @@ All 16 are Data Asset definitions. No code — create Data Asset instances per c
|---|--------|------|--------------| |---|--------|------|--------------|
| 104 | `BPC_AccessibilitySettings` | BP child | Accessibility: colorblind, subtitle, control remapping, TTS | | 104 | `BPC_AccessibilitySettings` | BP child | Accessibility: colorblind, subtitle, control remapping, TTS |
| 105 | `SS_SettingsSystem` | BP child of GameInstanceSubsystem | Settings persistence: save/load config to disk, apply on boot | | 105 | `SS_SettingsSystem` | BP child of GameInstanceSubsystem | Settings persistence: save/load config to disk, apply on boot |
| 148 | `BPC_HapticsController` | BP child of ActorComponent | Haptics: GameplayTag-driven force feedback, DualSense triggers, heartbeat pulse |
--- ---

View File

@@ -13,17 +13,20 @@
--- ---
# 12 — Settings, Accessibility & Platform Systems (Systems 104-105) # 12 — Settings, Accessibility, Haptics & Platform Systems (Systems 104-105, 148)
| # | System | Asset Type | Role | | # | System | Asset Type | Role |
|---|--------|-----------|------| |---|--------|-----------|------|
| 104 | `BPC_AccessibilitySettings` | Component | Accessibility; subtitles, colorblind, controller remap | | 104 | `BPC_AccessibilitySettings` | Component | Accessibility; subtitles, colorblind, controller remap |
| 105 | `SS_SettingsSystem` | Subsystem | Settings subsystem; persistent settings, apply, reset | | 105 | `SS_SettingsSystem` | Subsystem | Settings subsystem; persistent settings, apply, reset |
| 148 | `BPC_HapticsController` | Component | Haptics controller; GameplayTag-driven force feedback, DualSense triggers, heartbeat pulse |
**104 BPC_AccessibilitySettings:** Manages accessibility features: subtitle toggle/size, colorblind mode selection (protanopia/deuteranopia/tritanopia), controller remapping, difficulty presets, hold-to-confirm toggle, camera shake reduction. Settings persisted via `SS_SettingsSystem`. **104 BPC_AccessibilitySettings:** Manages accessibility features: subtitle toggle/size, colorblind mode selection (protanopia/deuteranopia/tritanopia), controller remapping, difficulty presets, hold-to-confirm toggle, camera shake reduction. Settings persisted via `SS_SettingsSystem`.
**105 SS_SettingsSystem:** GameInstanceSubsystem for all persistent settings. Loads defaults on init, saves on change, supports reset-to-default. Settings categories: Audio, Video, Controls, Gameplay, Accessibility. Coordinates with `SS_AudioManager` for volume, `SS_EnhancedInputManager` for key rebinding. **105 SS_SettingsSystem:** GameInstanceSubsystem for all persistent settings. Loads defaults on init, saves on change, supports reset-to-default. Settings categories: Audio, Video, Controls, Gameplay, Accessibility. Coordinates with `SS_AudioManager` for volume, `SS_EnhancedInputManager` for key rebinding.
**148 BPC_HapticsController:** ActorComponent attached to the Player Controller. Central abstraction for all controller vibration and force feedback. Systems trigger haptics by GameplayTag (e.g., `Haptic.Damage.Heavy`) — never calling raw UE5 `Play Force Feedback` nodes. Handles platform detection (Xbox rumble, PS5 DualSense adaptive triggers, PC generic gamepad), respects accessibility toggle, manages effect priority/conflict resolution, and drives the continuous heartbeat pulse from `BPC_StateManager.GetCurrentHeartRate()`. Uses `DA_HapticProfile` Data Assets for all effect definitions.
--- ---
# 13 — Polish: Tutorial, Loading, Credits & Debug Systems (Systems 106-114) # 13 — Polish: Tutorial, Loading, Credits & Debug Systems (Systems 106-114)
@@ -108,7 +111,7 @@
--- ---
*Developer Reference v1.0 — Categories 11-16 Systems. Companion to docs/blueprints/ specs.* *Developer Reference v1.1 — Categories 11-16 Systems (including Haptics). Companion to docs/blueprints/ specs.*
--- ---
@@ -121,6 +124,7 @@
| `SS_AchievementSystem` | Server validates unlocks; client receives unlock notifications | | `SS_AchievementSystem` | Server validates unlocks; client receives unlock notifications |
| `BPC_AccessibilitySettings` | **Local per-client** each player's accessibility preferences | | `BPC_AccessibilitySettings` | **Local per-client** each player's accessibility preferences |
| `SS_SettingsSystem` | Settings save per-client; key bindings are local | | `SS_SettingsSystem` | Settings save per-client; key bindings are local |
| `BPC_HapticsController` | **Local client only** haptics are cosmetic, never replicated. No `HasAuthority()` needed. |
### Category 13: Polish ### Category 13: Polish
| System | Authority | | System | Authority |

View File

@@ -1,6 +1,6 @@
# Developer Reference — UE5 Modular Game Framework # Developer Reference — UE5 Modular Game Framework
**Version:** 1.6 | **Generated:** 2026-05-22 | **Files:** 19 (1 index + 2 overview + 1 migration + 1 integration + 1 prototype + 1 starter + 10 category docs + 1 combined + 1 capture) | **C++:** 15 full + 10 stubs = 25 systems **Version:** 1.7 | **Generated:** 2026-05-22 | **Files:** 20 (1 index + 2 overview + 1 migration + 1 integration + 1 prototype + 1 starter + 10 category docs + 1 combined + 1 capture + 1 haptics) | **C++:** 15 full + 10 stubs = 25 systems
This directory contains developer-facing reference documentation for every system in the framework. Unlike the blueprint spec files (which define *what* to build), these documents explain *how each system works internally* — the data flow, state machines, integration points, and design rationale. Use these when you need to understand a system's behavior to implement, debug, or extend it. This directory contains developer-facing reference documentation for every system in the framework. Unlike the blueprint spec files (which define *what* to build), these documents explain *how each system works internally* — the data flow, state machines, integration points, and design rationale. Use these when you need to understand a system's behavior to implement, debug, or extend it.
@@ -34,6 +34,10 @@ docs/developer/
├── 10-adaptive-systems.md ← Adaptive environment & atmosphere (systems 89-101, 132-133) ├── 10-adaptive-systems.md ← Adaptive environment & atmosphere (systems 89-101, 132-133)
├── 11-16-systems.md ← Meta, Settings, Polish, Data Assets, Input, State (systems 102-135) ├── 11-16-systems.md ← Meta, Settings, Polish, Data Assets, Input, State (systems 102-135)
└── 17-capture-systems.md ← Planar Capture System — Mirrors, Portals, Monitors, Horror (systems 136-147) └── 17-capture-systems.md ← Planar Capture System — Mirrors, Portals, Monitors, Horror (systems 136-147)
## Haptics Reference
- **18-haptics-system.md** — Haptics Controller — Force feedback, DualSense triggers, heartbeat pulse, platform profiles (system 148)
- **game/haptics-example.md** — Project Void haptics walkthrough — wiring all game systems, platform tuning, testing checklist
``` ```
## Quick Reference — Every System at a Glance ## Quick Reference — Every System at a Glance
@@ -146,6 +150,7 @@ docs/developer/
| 103 | `SS_AchievementSystem` | Meta | Achievement subsystem | | 103 | `SS_AchievementSystem` | Meta | Achievement subsystem |
| 104 | `BPC_AccessibilitySettings` | Settings | Accessibility settings | | 104 | `BPC_AccessibilitySettings` | Settings | Accessibility settings |
| 105 | `SS_SettingsSystem` | Settings | Persistent settings subsystem | | 105 | `SS_SettingsSystem` | Settings | Persistent settings subsystem |
| 148 | `BPC_HapticsController` | Settings | Haptics controller — force feedback, DualSense triggers, heartbeat pulse (NEW) |
| 106 | `BPC_AnalyticsTracker` | Polish | Analytics/telemetry | | 106 | `BPC_AnalyticsTracker` | Polish | Analytics/telemetry |
| 107 | `BPC_DevCheatManager` | Polish | Developer cheats | | 107 | `BPC_DevCheatManager` | Polish | Developer cheats |
| 108 | `BPC_ErrorHandler` | Polish | Error/crash handling | | 108 | `BPC_ErrorHandler` | Polish | Error/crash handling |
@@ -176,6 +181,7 @@ docs/developer/
| 133 | `BP_RoomAudioZone` | Adaptive | Room audio zone trigger | | 133 | `BP_RoomAudioZone` | Adaptive | Room audio zone trigger |
| 134 | `DA_AudioSettings` | Data Assets | Audio bus/settings config | | 134 | `DA_AudioSettings` | Data Assets | Audio bus/settings config |
| 135 | `DA_RoomAcousticPreset` | Data Assets | Room acoustic profile | | 135 | `DA_RoomAcousticPreset` | Data Assets | Room acoustic profile |
| 148 | `BPC_HapticsController` | Settings | Haptics controller — force feedback, DualSense triggers, heartbeat pulse |
## How to Use These Docs ## How to Use These Docs
@@ -201,4 +207,4 @@ docs/developer/
--- ---
*Developer Reference Index v1.4 — Companion to the Blueprint Spec system. Update both together.* *Developer Reference Index v1.5 — Companion to the Blueprint Spec system. Update both together.*

View File

@@ -36,6 +36,9 @@ Direct Reference │ Interface Calls
│ LAYER 8: POLISH & META │ │ LAYER 8: POLISH & META │
│ Tutorials, Loading, Credits, Analytics, Achievements │ │ Tutorials, Loading, Credits, Analytics, Achievements │
├────────────────────────────────────────────────────────────────┤ ├────────────────────────────────────────────────────────────────┤
│ LAYER 7.5: HAPTICS │
│ Controller vibration, DualSense triggers, heartbeat pulse │
├────────────────────────────────────────────────────────────────┤
│ LAYER 7: ADAPTIVE │ │ LAYER 7: ADAPTIVE │
│ Difficulty, Atmosphere, Pacing, Scares, Audio (MetaSounds) │ │ Difficulty, Atmosphere, Pacing, Scares, Audio (MetaSounds) │
├────────────────────────────────────────────────────────────────┤ ├────────────────────────────────────────────────────────────────┤
@@ -86,7 +89,10 @@ Never call `UGameplayStatics::PlaySound*` directly. All audio routes through `SS
### 7. Enhanced Input Manager ### 7. Enhanced Input Manager
All input goes through `SS_EnhancedInputManager`. Context switching uses priority-based stack. Key rebinding is platform-aware via `DA_InputMappingProfile`. All input goes through `SS_EnhancedInputManager`. Context switching uses priority-based stack. Key rebinding is platform-aware via `DA_InputMappingProfile`.
### 8. Force Stack for State Overrides ### 8. GameplayTag-Driven Haptics
All controller vibration routes through `BPC_HapticsController` on the Player Controller. Gameplay systems trigger haptics by GameplayTag (e.g., `Haptic.Damage.Heavy`) — never calling raw `Play Force Feedback` nodes. The component detects platform (Xbox rumble vs PS5 DualSense adaptive triggers), respects accessibility toggles, and manages effect priority. Heartbeat pulse is driven by `BPC_StateManager.GetCurrentHeartRate()`.
### 9. Force Stack for State Overrides
Death, cutscenes, void space push state onto a stack. `RestorePreviousState()` pops back — the player returns to exactly their previous state after a forced interruption. Death, cutscenes, void space push state onto a stack. `RestorePreviousState()` pops back — the player returns to exactly their previous state after a forced interruption.
--- ---
@@ -144,12 +150,13 @@ The 16 build phases follow dependency order:
| `WBP_` Widget Blueprints | 14 | HUDController, InventoryMenu, MainMenu | | `WBP_` Widget Blueprints | 14 | HUDController, InventoryMenu, MainMenu |
| `DA_` Data Assets | 18 | ItemData, EquipmentConfig, AtmosphereProfile | | `DA_` Data Assets | 18 | ItemData, EquipmentConfig, AtmosphereProfile |
| `SS_` GameInstance Subsystems | 7 | SaveManager, UIManager, AudioManager | | `SS_` GameInstance Subsystems | 7 | SaveManager, UIManager, AudioManager |
| `BPC_` Components (haptics) | 1 | HapticsController (PlayerController) |
| `GI_` Game Instances | 2 | GameFramework, GameTagRegistry | | `GI_` Game Instances | 2 | GameFramework, GameTagRegistry |
| `I_` Interfaces | 3 | InterfaceLibrary, HidingSpot, Persistable | | `I_` Interfaces | 3 | InterfaceLibrary, HidingSpot, Persistable |
| `GM_` GameMode, `GS_` GameState | 2 | CoreGameMode, CoreGameState | | `GM_` GameMode, `GS_` GameState | 2 | CoreGameMode, CoreGameState |
| `FL_` Function Library | 1 | GameUtilities | | `FL_` Function Library | 1 | GameUtilities |
| `AI_` Controller, `BB_` Blackboard | 2 | BaseAgentController, AgentBoard | | `AI_` Controller, `BB_` Blackboard | 2 | BaseAgentController, AgentBoard |
| **TOTAL** | **140** | | | **TOTAL** | **141** | |
--- ---

View File

@@ -583,11 +583,12 @@ Each game asset proves a specific framework system works. Below: every framework
| 102 | BPC_ProgressStatTracker | Total playtime, collectibles found, enemies killed | | 102 | BPC_ProgressStatTracker | Total playtime, collectibles found, enemies killed |
| 103 | SS_AchievementSystem | "First Light" (flashlight), "Untouchable" (no-damage run) | | 103 | SS_AchievementSystem | "First Light" (flashlight), "Untouchable" (no-damage run) |
### 12-settings (Settings — 2 systems) ### 12-settings (Settings — 3 systems)
| # | Framework System | Demonstrated By | | # | Framework System | Demonstrated By |
|---|----------------|----------------| |---|----------------|----------------|
| 104 | BPC_AccessibilitySettings | Subtitle toggle, colorblind mode, controller remap | | 104 | BPC_AccessibilitySettings | Subtitle toggle, colorblind mode, controller remap |
| 105 | SS_SettingsSystem | Audio/Video/Controls persistent settings | | 105 | SS_SettingsSystem | Audio/Video/Controls persistent settings |
| 148 | BPC_HapticsController | Controller vibration: damage, weapon fire, heartbeat, footsteps, scares. Attached to PC_HorrorController |
### 13-polish (Polish — 9 systems) ### 13-polish (Polish — 9 systems)
| # | Framework System | Demonstrated By | | # | Framework System | Demonstrated By |
@@ -691,6 +692,7 @@ Each `docs/game/` file explains how to build a specific section of the prototype
| [`save-checkpoints.md`](save-checkpoints.md) | Save system, checkpoints, death loop, void space, persistence | 15 | | [`save-checkpoints.md`](save-checkpoints.md) | Save system, checkpoints, death loop, void space, persistence | 15 |
| [`polish-loading-credits.md`](polish-loading-credits.md) | Tutorials, loading screen, credits, debug, analytics | 19, 20 | | [`polish-loading-credits.md`](polish-loading-credits.md) | Tutorials, loading screen, credits, debug, analytics | 19, 20 |
| [`state-gating-examples.md`](state-gating-examples.md) | DA_StateGatingTable game-specific rules | 14 | | [`state-gating-examples.md`](state-gating-examples.md) | DA_StateGatingTable game-specific rules | 14 |
| [`haptics-example.md`](haptics-example.md) | Controller haptics — damage, weapons, heartbeat, scares, platform tuning | 5, 10, 13, 14 |
--- ---

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).*