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:
@@ -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
|
||||||
@@ -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)*
|
||||||
|
|||||||
577
docs/blueprints/12-settings/148_BPC_HapticsController.md
Normal file
577
docs/blueprints/12-settings/148_BPC_HapticsController.md
Normal file
@@ -0,0 +1,577 @@
|
|||||||
|
# 148 — Haptics Controller (`BPC_HapticsController`)
|
||||||
|
|
||||||
|
> **Blueprint-Only Implementation** — UE 5.5–5.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.0–2.0, 1.0 = default) |
|
||||||
|
| `DurationOverride` | `Float` | Override duration (-1 = use profile default) |
|
||||||
|
| `Priority` | `Int32` | Higher interrupts lower (0–100) |
|
||||||
|
| `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 (0–9) |
|
||||||
|
| `Strength` | `Int32` | Effect strength (0–8) |
|
||||||
|
- **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.*
|
||||||
@@ -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` |
|
||||||
|
|||||||
@@ -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.0–1.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.0–1.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 (0–9) |
|
||||||
|
| `DualSense_TriggerStrength` | `int32` | PS5 trigger effect strength (0–8) |
|
||||||
|
|
||||||
|
### 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 0–100
|
||||||
|
- 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: 0–20 (ambient/footsteps), 30–50 (weapons/reload/pickups), 60–80 (damage), 90–100 (scares/death).
|
||||||
|
- The `Haptic.` namespace should be registered in `DT_Tags_Player.csv` so `DA_GameTagRegistry` validates them at startup.
|
||||||
|
|||||||
@@ -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) |
|
||||||
|
|
||||||
|
|||||||
@@ -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)` |
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -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.*
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -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 |
|
||||||
|
|||||||
@@ -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.*
|
||||||
|
|||||||
@@ -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** | |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -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 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
438
docs/game/haptics-example.md
Normal file
438
docs/game/haptics-example.md
Normal file
@@ -0,0 +1,438 @@
|
|||||||
|
# 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).*
|
||||||
Reference in New Issue
Block a user