Skip to content
EN

Fire Haptics from a Script

This page shows how to fire haptics from inside your own game logic (took damage, a bullet hit, picked up an item…).

There are only three things to do.

  1. Create an entry in the EventMap for the haptic you want to play
  2. Add a Hapbeat UnityEvent Trigger to a GameObject in the scene, and select the EventMap and entry in the Inspector
  3. Hold a reference to that Trigger in your own script and call Fire() where you want the haptic to play
using UnityEngine;
using Hapbeat; // the SDK namespace
public class PlayerHealth : MonoBehaviour
{
// Drag the Trigger from step 2 onto this field in the Inspector
[SerializeField] private HapbeatUnityEventTrigger _damageHaptic;
public void TakeDamage(int amount)
{
// ...reduce HP...
_damageHaptic.Fire(); // play the haptic
}
}

Which waveform to play, at what intensity, and on which device is decided not by the script but by the EventMap entry. The script only says “play now”, so you do not need to rewrite code to change the intensity or destination.

How it works — where does the EventMap live?

Section titled “How it works — where does the EventMap live?”

The EventMap does not appear in the code because the Trigger component holds the EventMap and entry selection.

PlayerHealth (your script)
└ _damageHaptic ──reference──▶ HapbeatUnityEventTrigger (a component in the scene)
├ Event Map : MyScene-EventMap.asset ← chosen in the Inspector
└ Event : player_damage ← chosen in the Inspector
│
▼
EventMap entry (Mode / Clip / Gain / Target)
│
▼
HapbeatManager → Hapbeat device
  • HapbeatUnityEventTrigger is a MonoBehaviour provided by the SDK. It has public methods such as Fire() / Stop() (list: Script API Reference).
  • “UnityEvent” is in its name because you can also call Fire() from a UnityEvent such as a Button’s OnClick without code. The same component is used when calling from a script.
  • Sending is done by the HapbeatManager on [Hapbeat Event Router]. You do not need to look up the Manager from your script or write any connection handling.

Open Hapbeat → Open Event Map, add an entry with the + button in the toolbar, and set the following.

ItemExample setting
Nameplayer_damage (the name shown in the Inspector dropdown)
ModeThe easy way to start is CLIP (Stream Clip) with a WAV assigned to Clip (no Kit deploy needed)
GainStart around 0.5
TargetingLeave empty (all devices)

For the meaning of each item, see EventMap Window. The ▶ button on a row lets you try it without entering Play mode.

  1. Select any GameObject in the Hierarchy (the player itself or [Hapbeat Event Router] both work)
  2. Add Component → Hapbeat → Hapbeat UnityEvent Trigger
  3. In the Inspector, select the EventMap from step 1 in Event Map, and player_damage in the Event dropdown

When you have several haptics to play, place one Trigger per entry (several can be on the same GameObject).

Prepare a [SerializeField] field as in the code at the top, and drag the Trigger from step 2 onto it in the Inspector. After that, just call Fire() where you want it to play.

Press Play, and if the device vibrates when TakeDamage() is called, you are done.

Put a multiplier (0–2) in GainMultiplier and then Fire(). The final intensity is “entry Gain × Kit intensity × GainMultiplier”.

// The more damage taken, the stronger
_damageHaptic.GainMultiplier = Mathf.Clamp01(amount / 50f);
_damageHaptic.Fire();

Set the entry to CLIP (Stream Clip) + Loop, and call Fire() to start and Stop() to end.

[SerializeField] private HapbeatUnityEventTrigger _engineHaptic; // a Loop entry
void StartEngine() => _engineHaptic.Fire();
void StopEngine() => _engineHaptic.Stop();

Change intensity and left / right balance while it plays

Section titled “Change intensity and left / right balance while it plays”

Rewriting GainMultiplier or Pan (−1 = left to +1 = right) during the loop is reflected in the playing vibration immediately.

void Update()
{
// The faster the speed, the stronger
_engineHaptic.GainMultiplier = Mathf.InverseLerp(0f, 30f, _speed);
}

The same can be done without code using Parameter Binding (→ Parameter Binding).

To handle the three stages of a one-shot at the start, a continuous loop, and a one-shot at the end together, use the Hapbeat Sequence Trigger. From a script you again just call Fire() (grabbed) and Stop() (released).

[SerializeField] private HapbeatSequenceTrigger _grabHaptic;
void OnGrab() => _grabHaptic.Fire(); // On Start one-shot → Loop starts
void OnRelease() => _grabHaptic.Stop(); // Loop stops → On Stop one-shot
_damageHaptic.TriggerEnabled = false; // while false, Fire() does not play

The Showcase sample has three examples of calling haptics from a script. Reading them in order of difficulty is recommended.

ExampleScriptWhat it does
Z3 FishingFishingController.cs_sequence.Fire() when the mouse is pressed, _sequence.Stop() when released. These two lines are all the haptics-related code
Z4 Stream ConsoleStreamDemoController.csToggles Fire() / Stop() of HapbeatUnityEventTrigger with the Space key, and uses ActivePlayback to tell whether it is playing
Z5 Charge & ShootChargeShooter.csAn advanced example that reads EventMap entries and calls HapbeatManager directly, without a Trigger (below)

To open the Showcase, see Showcase Sample.

As an advanced method, you can also call HapbeatManager directly without placing a Trigger component.

// On a device with a Kit already deployed, fire by Event ID (FIRE / Command)
HapbeatManager.Instance?.Play("my-kit.enemy_hit", gain: 0.8f);
// Send a Unity AudioClip as is and play it (CLIP / Stream)
HapbeatStreamPlayback pb = HapbeatManager.Instance?.StreamAudioClip(myClip, gain: 0.5f);
pb?.Stop(); // stop it / change its intensity with the returned handle

However, on this path the Gain, Target, Kit intensity compensation, and latency compensation set in the EventMap do not apply (you pass every value from code). As a rule of thumb, use it only in cases such as:

  • Building the Event ID at runtime (e.g. a different ID per player like $"heartbeat.player_{id}")
  • There are too many entries to manage in the EventMap

For a way of calling the Manager directly while still making use of the EventMap settings, the Showcase’s ChargeShooter.cs serves as a model as it is (it takes out an entry with _eventMap.FindById(...) and passes entry.GetEffectiveGain() / entry.target / entry.streamClip to the Manager).

SymptomWhat to check
Nothing happens on Fire()Whether [Hapbeat] Fire rejected: no EventMap assigned appears in the Console (no Event Map selected in step 2)
entry id '...' not found warningThe entry was deleted from the EventMap. Pick the entry again in the Trigger’s Event field
Plays the first time but not when pressed repeatedlyThe Trigger’s Cooldown is greater than 0
Exception because _damageHaptic is nullThe Trigger was not assigned to the field in the Inspector
CLIP plays but FIRE does notThe Kit is not deployed to the device (Fire vs. Clip — When to Use Each)