Files
UE5-Modular-Game-Framework/docs/blueprints/12-settings/148_BPC_HapticsController.md
Lefteris Notas 15d6e88780 feat: Implement BPC_PlatformServiceAbstraction for unified platform detection and SDK routing
- Added BPC_PlatformServiceAbstraction to centralize platform detection and SDK routing for achievements, cloud saves, and overlays.
- Updated dependencies across various systems to utilize the new platform service for consistent platform handling.
- Deprecated old platform enums in favor of a unified EPlatformFamily enum.
- Enhanced documentation for affected systems to reflect changes in platform handling and dependencies.
2026-05-22 18:22:42 +03:00

27 KiB
Raw Blame History

148 — Haptics Controller (BPC_HapticsController)

Blueprint-Only Implementation — UE 5.55.7 fully supports controller haptics/force feedback from Blueprints. No C++ required. This component wraps UE5's Play Force Feedback, Set Haptics By Value, and DualSense adaptive trigger APIs behind a GameplayTag-driven event system.


Purpose

Abstraction layer for all controller haptic feedback and force feedback effects. Gameplay systems trigger haptics by GameplayTag (e.g., Haptic.Damage.Heavy) rather than calling raw UE5 haptic APIs. This component handles platform detection (Xbox rumble vs PS5 DualSense adaptive triggers vs generic PC gamepad), respects accessibility settings (BPC_AccessibilitySettings.bHapticsEnabled), and manages effect priority/conflict resolution.

Dependencies

  • Requires: DA_HapticProfile (effect definitions), BPC_AccessibilitySettings (master toggle), BPC_StateManager (heart rate for heartbeat haptic), BPC_PlatformServiceAbstraction (platform detection — replaces own platform enum)
  • 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 (deprecated — use EPlatformFamily from BPC_PlatformServiceAbstraction. This enum is mapped internally from the unified platform enum.)

Value Description
Unknown = 0 Platform not yet detected
PC_Generic = 1 PC with generic gamepad (XInput)
Xbox = 2 Xbox Series X|S / Xbox One controller
PS5_DualSense = 3 PlayStation 5 DualSense controller
PS4_DualShock = 4 PlayStation 4 DualShock 4 controller

2. Structs

S_HapticRequest

Field Type Description
ProfileTag FGameplayTag Which DA_HapticProfile to use
EventType EHapticEvent Event category
IntensityMultiplier Float Scale intensity (0.02.0, 1.0 = default)
DurationOverride Float Override duration (-1 = use profile default)
Priority Int32 Higher interrupts lower (0100)
RequestTime Float Game time when requested (for cooldown)

3. Variables

Configuration (Instance Editable)

Variable Type Default Category Description
HapticProfileMap TMap<FGameplayTag, DA_HapticProfile> Empty Config All haptic profiles loaded at startup
bHapticsEnabled Bool true Config Master toggle (synced from BPC_AccessibilitySettings)
bEnableDualSenseTriggers Bool true Config Enable adaptive trigger effects on PS5
bEnableSpeakerAudio Bool true Config Enable controller speaker audio on PS5
MinTimeBetweenEffects Float 0.05 Config Minimum seconds between any two effects (prevents rumble spam)
HapticIntensityScale Float 1.0 Config Global intensity multiplier (0.0 = off, 1.0 = full)
HeartbeatMinBPM Float 40.0 Config Minimum BPM for heartbeat haptic
HeartbeatMaxBPM Float 180.0 Config Maximum BPM for heartbeat haptic

Internal (Private)

Variable Type Default Category Description
CurrentPlatform EControllerPlatform Unknown State Detected controller platform
bIsInitialized Bool false State Whether Initialize has completed
CachedPlayerController APlayerController None Cache Cached owner PlayerController reference
ActiveHapticEffect UForceFeedbackEffect None State Currently playing effect asset
LastPlayTime Float 0.0 State Game time of last played effect
PendingRequest S_HapticRequest Empty State Currently queued request
bHeartbeatActive Bool false State Whether heartbeat loop is active

4. Functions

Public Functions

Initializevoid

  • 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

PlayHapticByTagvoid

  • 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

PlayHapticByEventvoid

  • 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., DamageHaptic.Damage.Default) and calls PlayHapticByTag.

StopHapticvoid

  • 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

PlayHeartbeatHapticvoid

  • 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 HeartbeatMinBPMHeartbeatMaxBPM
    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

StopHeartbeatHapticvoid

  • Description: Stop the continuous heartbeat haptic loop.
  • Flow: Clear heartbeat timer, set bHeartbeatActive = false.

SetHapticsEnabledvoid

  • 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

SetControllerPlatformvoid

  • Description: Force a specific controller platform (for testing or hot-swap).
  • Parameters:
    Param Type Description
    Platform EControllerPlatform Target platform

GetControllerPlatformEControllerPlatform

  • Description: Returns the detected controller platform. Read-only.

IsDualSenseConnectedBool

  • Description: Returns true if a PS5 DualSense controller is detected.
  • Flow: Returns CurrentPlatform == PS5_DualSense

SetDualSenseTriggerEffectvoid

  • Description: Set adaptive trigger resistance on a specific DualSense trigger. No-op on non-DualSense platforms.
  • Parameters:
    Param Type Description
    TriggerSide Name "Left" or "Right"
    EffectType Name "Resistance", "Vibration", "WeaponFire", "BowDraw", etc.
    StartPosition Int32 Trigger position where effect begins (09)
    Strength Int32 Effect strength (08)
  • Blueprint Authority: Local Client Only
  • Flow:
    1. If CurrentPlatform != PS5_DualSense: return (graceful no-op)
    2. If bEnableDualSenseTriggers == false: return
    3. Call platform-specific DualSense trigger API via PlayerController
    4. Log effect for debugging

Protected / Private Functions

PlayHapticInternalvoid

  • 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

DetectControllerPlatformvoid

  • 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

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.