SDK Architecture & API
How the HEXR Unity SDK is put together, how the glove connects, how interactions become haptics, and how to drive the glove from your own code.
This page describes com.microtube.hexr version 0.9.4, released 11 October 2026. See the changelog for earlier releases.
Package structure
The package is a stack. Your scene talks to the top; the glove sits at the bottom. The two backend layers only compile when their XR SDK is installed, and the core never depends on them.
| Assembly | Namespace | Holds | Compiles when |
|---|---|---|---|
HaptGlove.Runtime | HaptGlove | HaptGloveHandler, Bluetooth transports, Haptics command builders | Always |
HexR.Runtime | HexR | Manager, hand tracking, Pressure Controllers, touch points, scene components, HexR menu | Always |
HexR.Runtime.MetaOVR | HexR.MetaOVR | Meta grab and poke detection, interactable events, passthrough | Meta Interaction SDK installed |
HexR.Runtime.OpenXR | HexR.OpenXR | XR Interaction Toolkit integration (OpenXR headsets, including PICO) | XR Interaction Toolkit installed |
HexR.Editor | HexR | HexR menu, Auto Setup, validator, HexR Tools window | Editor only |
Backends register themselves with the core when they load. The collider visualizer, HexRUsable and the HexR menu stay in the global namespace so older scenes keep working.
The HEXR scene
HexR → Create HexR Rig adds the first two. Your XR SDK provides the third.
| Object | Components | Role |
|---|---|---|
| HexR Main | HexRManager | Owns both gloves, runs connections, re-attaches the hands after a scene change. Reach it as HexRManager.Instance |
| Left / Right Hand Physics | HaptGloveHandler, HexRTrackedHand, FingerUseTracking | One per hand: the Bluetooth link, which tracked hand it follows, how bent each finger is |
| HexR Floating Menu | HexRFloatingMenu | The HexR menu, in the headset: connect gloves, show touch points, watch each finger’s haptics, switch scenes and demos. Pressed with a fingertip |
| Left / Right Pressure Controller | PressureTrackerMain | The haptics controller for that hand |
The touch points find them by name when the scene starts. A hand without one produces no haptics. HexR → Add Pressure Controller adds them.
The tracked hand
HexRTrackedHand.handRoot points at your SDK’s hand. HEXR finds the joints by name:
| XR SDK | Hand root | Joints |
|---|---|---|
| Meta Interaction SDK | OpenXRLeftHand / OpenXRRightHand | XRHand_IndexTip, XRHand_Palm, … |
| XR Interaction Toolkit + XR Hands | L_Wrist / R_Wrist under Left / Right Hand Interaction Visual | L_IndexTip, L_Palm, … |
Each touch point is a small trigger collider, a kinematic Rigidbody (Unity needs one for trigger events) and a HapticFingerTrigger that says which hand and finger it is. HEXR has no hand of its own: since 0.9 the touch points sit on the tracked hand on every backend. An older project with a “ghost” hand can remove it with HexR → Migration → Remove Ghost Hand.
Connecting the glove
Your app opens the Bluetooth connection itself. No pairing in system settings, no companion app. Each glove has its own HaptGloveHandler.
Connect(hand) from your code, or the menu’s Connect button- Only one hand scans at a time; the second glove waits for the first.
- On Android a failed attempt is retried with a growing delay for up to a minute, and a glove that drops out is reconnected. Pressing the menu’s Connect again cancels a retry;
Connect(hand)never does. - Disconnecting, quitting and pausing release the glove first. See Releasing the glove.
| Where your app runs | Bluetooth |
|---|---|
| Android headsets — Meta Quest, PICO | Supported; the rig’s default |
| Windows — Unity Editor and 64-bit builds | Supported. In a build, set Build Platform on each HaptGloveHandler to Window. In the Editor, connect from the HexR Haptics Tester, which does this for Play mode |
| macOS, iOS, Linux, HoloLens (UWP) | Not supported by the package. A macOS Bluetooth library ships inside it, but nothing calls it yet |
The whole list, headsets included, is in Platform & headset support.
Events
Public UnityAction fields on HaptGloveHandler. Subscribe with += and unsubscribe with -=. Never assign with =: that removes the handlers HexRManager relies on. They are not UnityEvents, so they do not show in the Inspector.
| Event | When |
|---|---|
onBluetoothConnected(HandType) | The glove connected |
onBluetoothConnectionFailed(HandType) | An attempt failed |
onBluetoothDisconnected(HandType) | The link closed: the glove dropped out, you disconnected it, or the app is shutting down |
onPumpAction(HandType, bool) | The air supply started or stopped |
onBatteryStateChange(BatteryState) | Full, Medium or Low |
onDeviceFound(id, name) | A scan found a device |
HexRManager.ConnectAttemptEnded | Static. A connect attempt ended without a connection: the permission was refused, or an attempt failed, including one that will be retried. Not raised on success; use onBluetoothConnected for that |
How haptics fire
PressureTrackerMain for that handHaptGloveHandler builds and sends the command| Trigger | Use it for | Example |
|---|---|---|
| XR interaction events | Feeling an object while it is grabbed or hovered | HexRInteractableHaptics |
| Physics contact | Feeling a surface, a liquid, a heartbeat | SpecialHaptics |
| Your code | Game events, sequences, tests | PressureTrackerMain |
Latency
The time from an event in your app to a sensation is three delays added together. They are listed separately because only the last one is set by the glove; the other two depend on your setup.
| Stage | What happens | How long |
|---|---|---|
| 1 · Your app to the radio | On Quest and PICO builds, Quest BLE Buffering is on in both packaged rigs: commands are collected and sent as one Bluetooth write at most every 100 ms, checked on Unity’s fixed update. On Windows, and in Python, each command is written as soon as you send it | Up to about 100 ms on Quest and PICO with buffering on |
| 2 · Bluetooth | The glove asks for a 7.5 ms connection interval; the phone, headset or PC chooses the interval it actually uses. Android and Python write with a response from the glove | Set by the host; not measured |
| 3 · The glove | The pump starts driving within one 20 ms control cycle of a pressure command arriving, then ramps to the target. At the fastest speed (1) the ramp takes about 60 ms per 10 kPa: roughly 90 ms to the lightest intensity, 300 ms to the firmest, and ten times longer at speed 0.1. Vibration at 5 Hz and above starts on the next pass of the control loop. A release stops the pump at once | From the firmware source; not a bench measurement |
End-to-end latency — from your event to the moment the user feels it — has not been measured for the current glove, and no figure for it is published here. If your application depends on it, measure it on your own setup.
Buffering sends fewer, larger Bluetooth writes, at the cost of waiting up to 100 ms. Turning it off (the Quest BLE Buffering setting on HexRManager) writes every command immediately, but commands sent back to back can then be dropped on Android. It has only been tested on Quest; if a PICO build connects but feels laggy or silent, try it both ways.
The hand-near check
A Pressure Controller sends most haptics only while IsHandNear() is true. Any one of these makes it true:
| Signal | Set by |
|---|---|
| Grabbing, or hovering to grab | Your SDK’s grab interactor — Meta’s HandGrabInteractor, or XRI’s XRDirectInteractor or (from 0.9.4) NearFarInteractor. Wired for you by Auto Setup and on every scene load. For a Near-Far Interactor only a near grab counts, and its hover only with far casting off: an object grabbed along the ray from across the room does not press the glove |
| Poking, or about to | Your SDK’s poke interactor — Meta’s PokeInteractor or XRI’s XRPokeInteractor, wired the same way |
A fingertip inside a ProximityCheck volume | A ProximityCheck you add to an object your interactors do not cover. It stays set for 0.5 s after the fingertip leaves, its clearDelay default |
The three results are public fields on each PressureTrackerMain: HandGrabbing, PokeHovering and CollisionNearHand. The check itself, IsHandNear(), is private. It is per hand: one signal anywhere opens it for every gated call on that hand.
| Checked | Not checked |
|---|---|
Single…Haptic, TriggerAll…, TriggerCustom…, TriggerPinchPressure | HapticFingerTrigger.TriggerFixPressure, every SpecialHaptics effect, CustomSingleHaptics / CustomSingleVibrations with ByPassHandCheck set to true, RemoveAllHaptics, RemoveAllVibrations and the Remove…Haptics calls |
Releases are checked too when they go through a checked call: RemovePinchPressure(), and CustomSingleHaptics with false as the last argument, are dropped if the hand has already left. Release with RemoveAllHaptics(), a Remove…Haptics() call, or true as the last argument.
If your call does nothing, select the Pressure Controller, switch the Inspector to Debug and watch its three flags, or pass true as ByPassHandCheck while testing.
Channels and parameters
Six channels: Haptics.Finger.Thumb, Index, Middle, Ring, Pinky, Palm. Each plays one mode at a time. Values outside a range are clamped.
| Mode | Feels like | Parameters |
|---|---|---|
| Pressure | A steady press | intensity 0.1–1 (about 15–50 kPa); speed 0.1–1, how fast it ramps |
| Vibration | Pressure pulsing on and off | frequency 0.1–40 Hz; intensity 0.1–1. Below 5 Hz it sets pulse pressure, from 5 Hz pulse length |
| Slow pulse | A shaped, slow rhythm | frequency 0.1–2 Hz; intensity; peakRatio 0.2–0.8; speed; endIntensity (below 0.1 releases) |
| Counted pulses | A set number of pulses | frequency; intensity; pulseCount 1–1000 |
The scene components show their strength on a 10–60 scale — 10 is barely there, 60 is firm — and convert it to the 0.1–1 intensity above. Use PressureTrackerMain.StrengthToIntensity(strength) to do the same in your own code.
Releasing the glove
The glove keeps its last command until it is told otherwise; nothing on it lets go by itself. What the package does about that:
| When | What happens |
|---|---|
| A component is disabled or destroyed | HexRInteractableHaptics, HexRPhysicalButton, HexRGrabbable and SpecialHaptics release the channels they pressed. The last two from 0.9.4; a SpecialHaptics zone releases only its own channels, not another component’s |
| The app quits, including stopping Play mode in the Editor | Every channel on both gloves is released, then the link is closed. From 0.9.4 |
| The app pauses: the headset is taken off, or the app goes to the background | Every channel is released, while HaptGloveHandler.releaseWhenPaused is on (the default). Effects do not come back by themselves on resume; the next change re-applies them. From 0.9.4 |
You disconnect: Disconnect(hand), or the menu’s toggle | The glove is released first, then the link is closed. From 0.9.4 |
| The app crashes or is force-closed | Nothing can be sent. The glove keeps pressing |
| The Bluetooth link drops | Nothing can be sent. The glove keeps pressing, and the handler reconnects on Android without releasing. See Release on reconnect |
Switch the arm module off. A release on link loss would have to come from the glove’s firmware, which does not do that today. In your own code, release what you apply — in OnDisable too — or call ReleaseAllChannels() on the glove handler.
Scene components
Most projects never call the API. These components fire haptics on their own.
| Component | Use it for | Key fields |
|---|---|---|
HexRInteractableHaptics — start here | Feel your SDK’s own interactable (XRI or Meta) while grabbed or hovered. The backend is picked automatically. Releases its channels when disabled | fireOn Select / Hover / SelectAndHover; finger toggles; strength 10–60; followGrip, which scales it between minStrength and strength by how far the fingers have closed (both fixed in 0.9.4); backend |
SpecialHaptics | A trigger collider that plays a preset effect when touched. Ignores the hand-near check | RPressureTracker and LPressureTracker (required: Auto Find Hand Physics fills them), TypeOfHaptics (table below), HapticPressure, VibrationsFrequencyValue, InTimer / OutTimer |
ProximityCheck | Tell the Pressure Controller a hand is near an object that is not an SDK interactable | Auto Set Up button wires both Pressure Controllers |
HexRPhysicalButton | A button pressed by a real fingertip, no SDK needed. The HexR menu is built from it | onPressed; accepted fingers (index by default) |
HexRUsable | Triggers, handles, grips: raises events when chosen fingers curl. Both backends from 0.9.4; OpenXR only before | UseThreshold; WhenUseTriggerEvents / WhenStopUsingTriggerEvents |
HapticFingerTrigger | The touch point itself. Read it in your own OnTriggerEnter to learn which hand and finger touched | handType, fingertype; TriggerFixPressure, TriggerVibrationPressure, RemoveHaptics |
HexRGrabbable | HEXR's own grab, picked up by the glove hands with no interaction SDK. Sends straight to the glove, without the hand-near check. Before 0.9.4 it threw on start without a parent object and its haptics threw on grab | PinchGrab / PalmGrab; HapticStrength 10–60; needs a Rigidbody and a trigger collider |
SpecialHaptics effect | Feels like |
|---|---|
CustomHaptics | Constant pressure while touched |
CustomVibrations | Vibration at a set frequency and strength |
FountainEffect | Running water |
RainDropEffect | Raindrops on the palm |
HeartBeatEffect | A beating heart, regular or irregular |
HandSqueezeEffect | Fires OnSqueezeEventTrigger when the hand closes on the zone. Needs FingerUseTracking on the rig’s hands |
Which component do I need?
| You want… | Use | Good to know |
|---|---|---|
| To feel an object while your XR SDK grabs, pokes or hovers it | HexRInteractableHaptics beside the SDK’s interactable | Needs an XRI or Meta interactable on the object or a child, or it does nothing. Leave Backend on Auto |
| A surface or volume that feels like something — water, rain, a heartbeat, a buzz | SpecialHaptics on a trigger collider | Fires on contact alone and ignores the hand-near check, so it needs no ProximityCheck |
| A button pressed by a fingertip, without an SDK | HexRPhysicalButton | Needs a trigger collider |
| Something to happen when the fingers curl | HexRUsable | Works on Meta and OpenXR from 0.9.4. Earlier releases told the hands apart by the L_ / R_ joint names, which Meta’s skeleton does not use |
| Gated haptics on an object your rig’s grab and poke interactors do not cover | Add ProximityCheck | Press its Auto Set Up. On its own it fires nothing |
| Exact control from your own code | PressureTrackerMain methods | Release what you apply — see Triggering feedback |
Using components together
| Combination | Result |
|---|---|
HexRInteractableHaptics + ProximityCheck | Works. Only needed when your rig’s interactors do not feed the hand-near check, such as hover-triggered haptics on a Near-Far rig with far casting on, or any OpenXR rig before 0.9.4; otherwise redundant |
A SpecialHaptics zone on a grabbable object | Works. The PICO tutorial’s heart beats this way |
SpecialHaptics + ProximityCheck | Works but not needed. The zone ignores the check, and while a hand is inside, the ProximityCheck opens that hand’s check for every other gated call too |
HexRInteractableHaptics + UnityEvents on the same interactable calling a Pressure Controller | Avoid. Both drive the same channels and whichever ran last wins; the Inspector warns about it |
HexRInteractableHaptics + HexRGrabbable | Avoid. Two separate grab systems move the object and both send haptics |
The hand-near check is per hand, not per object: a grab, hover or ProximityCheck anywhere opens it for every gated call on that hand.
C# API
Call these when you want haptics driven by your own logic.
PressureTrackerMain — on each Pressure Controller
| Method | Does |
|---|---|
CustomSingleHaptics(finger, States, TargetPressure, Speed, ByPassHandCheck) | Pressure on one channel. States true applies, false releases; TargetPressure and Speed 0.1–1. Named arguments must use these exact names |
CustomSingleVibrations(finger, States, TargetPressure, frequency, ByPassHandCheck) | Vibration on one channel; frequency 0.1–40 Hz |
SingleThumbHaptic(float) … SinglePalmHaptic(float) | Pressure 0.1–1 on one channel. One float, so they wire to a UnityEvent |
RemoveThumbHaptics() … RemovePalmHaptics() | Release one channel |
TriggerAllHapticsIncrease(float) / TriggerAllVibrations(frequency) | Every channel at once |
TriggerCustomHapticsIncrease(bool[6], pressure, speed) / TriggerCustomlVibrations(bool[6], intensity, frequency) | A chosen set of channels, in Thumb…Palm order |
TriggerPinchPressure(float) / RemovePinchPressure() | Thumb-and-index pinch. The release is checked, so pair it with RemoveAllHaptics() if the hand may have left |
RemoveAllHaptics() / RemoveAllVibrations() | Release everything. Never blocked by the hand-near check; send both to be sure every channel is off |
HandGrabbing, PokeHovering, CollisionNearHand | The three inputs to the hand-near check. Read them; Auto Setup and the scene components set them |
StrengthToIntensity(strength) | Static. Converts the components’ 10–60 strength scale to the 0.1–1 intensity these methods take. From 0.9.4 |
TrackedHandRoot | The tracked hand this side of the rig follows, or null before one is found. From 0.9.4 |
HexRManager — HexRManager.Instance
| Member | Does |
|---|---|
Connect(hand) | Connect that glove, with the permission request and the one-hand-at-a-time rule. Does nothing if it is already connected or connecting, and needs no HexR menu. hand is HaptGloveHandler.HandType.Left or Right. From 0.9.4 |
Disconnect(hand) | Release that glove, then disconnect it. Does nothing if it is not connected. From 0.9.4 |
IsConnected(hand), IsConnecting(hand) | Connected right now; a connect under way, including waiting for permission or a retry. From 0.9.4 |
ConnectLeftBT() / ConnectRightBT() | What the menu’s buttons call. A toggle: on a connected glove it disconnects, and during a retry it cancels. From code, prefer Connect(hand). Before 0.9.4 these threw without the HexR menu in the scene |
leftHand / rightHand | Each glove’s HaptGloveHandler |
XRFramework | OpenXR or MetaOVR |
RebindHandsToCurrentScene() | Re-attach the touch points to this scene’s hands. Runs by itself on every scene load |
| Collider settings | Size and placement of the touch points Auto Setup adds, per skeleton |
ConnectAttemptEnded | Static event: a connect attempt ended without a connection. See Events |
HaptGloveHandler — one per glove, namespace HaptGlove
| Member | Does |
|---|---|
whichHand | Left or Right |
BTConnection() | Connect, or disconnect if connected. Prefer the manager’s Connect(hand), which adds permissions and the one-at-a-time rule |
IsConnecting | A connect is under way, including the wait between retries. From 0.9.4 |
ReleaseAllChannels(), ReleaseChannels(params Haptics.Finger[]) | Switch channels off, vibration and pressure alike, in one write. Nothing is sent while the glove is not connected. From 0.9.4 |
releaseWhenPaused | Release every channel when the app pauses. On by default. From 0.9.4 |
bleConnected | Connected right now? |
UseCustomDeviceName, OptionalCustomDeviceName | Look for a glove by a name you choose |
RememberDevice(id), ForgetDevice(), ConnectToDevice(id) | Which glove it goes straight to |
haptics | Builds commands: HEXRPressure, HEXRVibration, HEXRPulse. GetChannelState(finger) returns what the last command set on a channel, as a ChannelState (HapticMode, intensity, frequency) |
BTSend(byte[]) | Send a command straight to the glove. Skips the hand-near check |
GetBatteryLevel(), GetAirPressure() | Latest battery level and pressure readings |
HexRTrackedHand and FingerUseTracking — on each hand
| Member | Does |
|---|---|
HexRTrackedHand.handType, handRoot | Which hand, and the tracked hand it follows |
ResolveRawFingerJoint(finger), ResolveRawPalmJoint() | The tip or palm joint, on either skeleton |
FingerUseTracking.ThumbUse … LittleUse | 0 = fully curled, 1 = fully extended. Self-calibrating: settles once the hand has opened and closed |
FingerUseTracking.isHandOpen() | All fingers extended? |
Scene changes and editor tools
Your SDK’s hands die with their scene; the rig lives on. So after every scene load the manager re-attaches:
HapticFingerTrigger on each fingertip and the palmThen the log says [HexR] Rebound Left hand … 6 haptic trigger(s): five fingertips and the palm.
Auto Setup does the same in the Editor so it is saved with the scene. It only adds what is missing and never moves a collider you tuned. Run it from Create HexR Rig, HexR → Troubleshoot → Re-run Auto Setup, or the manager’s Auto Set Up HexR button. Validate Scene Setup then reports anything still unwired.
| Menu | Does |
|---|---|
| HexR → HexR Tools | The tools window, three tabs: HexR Haptics Tester (connect a glove in Play mode, fire any channel, watch live pressure, All OFF), HexR Setup (the validator as a checklist, with Fix all), Project Setup (pick OpenXR, Meta OVR or PICO; installs its packages) |
| HexR → Create Demo Scene | Rig, Pressure Controllers, a grabbable object and a haptic zone |
| HexR → Create HexR Rig | The rig and Pressure Controllers for your backend, then Auto Setup |
| HexR → Add Pressure Controller | The pair of Pressure Controllers |
| HexR → Add HexR Menu | The HexR menu in your own scene |
| HexR → Troubleshoot | Re-run Auto Setup, Validate Scene Setup |
| HexR → Migration | One-off upgrades, such as Remove Ghost Hand |
| HexR menu → Show Colliders | Draws every touch point on the tracked hands |
| HexR menu → HAPTICS | The value last sent to each finger and the palm of each glove, after it was clamped to the valid range: lit while on, dimmed after. Turn it off with showHapticsReadout |
Type index
Every public type in com.microtube.hexr, for looking a name up. Where a section above covers it, the link goes there.
Components
| Type | What it is |
|---|---|
HexRManager | The rig: both gloves, connections, re-attaching the hands. Members |
HaptGloveHandler | One glove’s Bluetooth link and command sender. Members |
PressureTrackerMain | A Pressure Controller: every haptic for one hand goes through it. Methods |
HexRTrackedHand, FingerUseTracking | Which tracked hand a glove follows, and how bent each finger is. Members |
HapticFingerTrigger | The touch point on each fingertip and the palm. Scene components |
HexRInteractableHaptics | Haptics on your SDK’s own interactable. Scene components |
HexRGrabbable | HEXR’s own grab, by the glove hands with no interaction SDK. Scene components |
SpecialHaptics, ProximityCheck, HexRPhysicalButton, HexRUsable | Preset effects, the hand-near volume, fingertip buttons, curl-to-use. Scene components |
HexRFloatingMenu | The in-headset panel. The HEXR scene |
HaptGloveCollidersVisualizer | Draws the touch points; what the menu’s Show Colliders uses. SetVisible(bool), IsShowing |
HaptGloveUI | On HexR Main: passes Connect presses on to the manager’s ConnectLeftBT() / ConnectRightBT(). Kept for older panels; it updates no text itself |
SceneLoader | Scene transitions for the app. Sits on the same persistent object as HexRManager |
HexRGripByHand | OpenXR: gives a stock XRGrabInteractable a grip per hand, and a pinch or fist hold, without replacing it. GripMode picks the hold |
OpenXRHandNearSource, MetaOVRHandNearSource | Feed the hand-near check from the grab and poke interactors. Auto Setup adds them |
OpenXRBackendSetup, MetaOVRBackendSetup | Wire those sources onto the Pressure Controllers, in the Editor and at run time. Nothing to add yourself |
MetaOVRPassthrough, IHexRPassthrough | Passthrough on Meta, and the interface the menu uses to refresh any backend’s passthrough after a scene load |
Enums and values
| Type | Values and where it is used |
|---|---|
Haptics | Command builders, on HaptGloveHandler.haptics. Channels and parameters |
Haptics.Finger | Thumb, Index, Middle, Ring, Pinky, Palm |
Haptics.HapticMode, Haptics.ChannelState | What the last command set on a channel: Off, Pressure, Vibration or Pulse, with intensity and frequency. From GetChannelState(finger) |
HandType | Left / Right, on HaptGloveHandler and on HapticFingerTrigger |
FingerType | Which finger a HapticFingerTrigger is, in fingertype |
BatteryState | Full, Medium, Low. Events |
TargetPlatform | A glove handler’s Build Platform: Android or Window (spelled without an s). Connecting the glove |
FireOn | HexRInteractableHaptics.fireOn: Select, Hover, SelectAndHover |
HexRBackendChoice | Which interaction SDK drives a component: Auto or a named backend |
HexRHandSide | Which hand an interaction came from, as a backend reports it |
HeartBeat | SpecialHaptics heartbeat: Regular or Irregular |
Options, Option | HexRGrabbable.Options PinchGrab / PalmGrab; HexRGrabbable.Option gravity On / Off; HexRManager.Options is XRFramework |
FingertipCenters | Per-finger touch point offsets in the manager’s collider settings |
Backend plumbing, editor and internals
| Type | What it is |
|---|---|
IHexRInteractionBackend, IHexRInteractionBinding, IHexRInteractionSink, HexRInteractionBackends, HexRHandSideResolver | How an XR SDK plugs into HEXR. Only needed to add a backend of your own |
HexRCompat | Stand-ins for Unity APIs renamed between 2022.3 and Unity 6. See Unity version support |
HexRMenu, HexRAutoSetup, HexRSetupValidator, SetupCheck, HexRToolsWindow | Editor: the HexR menu, Auto Setup, Validate Scene Setup and the HexR Tools window. Editor tools |
HexRSettingEditorGUI, HexRInteractableHapticsEditor, HapticEffectControllerEditor, ProximityCheckEditor, FingerUseTrackingEditorSetting | Editor: the Inspectors for HexRManager, HexRInteractableHaptics, SpecialHaptics, ProximityCheck and FingerUseTracking |
BLE, BLEScan, BLEData, Characteristic, Service, DeviceUpdate, ErrorMessage, ScanStatus, Encode, Decode, FunIndex, AndroidUiThread | Internal: the Bluetooth layer under HaptGloveHandler. Not for direct use; go through the handler or a Pressure Controller |
Python
Outside Unity, HaptGlovePython builds the same commands; you write them with bleak. It needs Python 3.8 to 3.13. Setup and a tested example are in the Python guide.
| Method | Does |
|---|---|
hexr_pressure(finger, state, intensity, speed) | Pressure on one channel. state True applies; False with intensity 0 releases |
hexr_pressure_multiple(fingers, states, intensities, speeds) | Pressure on several channels in one write; intensities and speeds 0.1–1. All states False and intensities 0 releases them |
hexr_vibrations(finger, state, frequency, intensity, peakRatio, speed, endIntensity) | The slow pulse, 0.1–2 Hz. hexr_vibrations_multiple takes lists |
AddressDiscovery.py, CharacteristicDiscovery.py, ExampleHaptics.py | Find your glove, list what it exposes, run a ready example |
Use Haptics.Finger.Index, not "Index". A string raises AttributeError.
Copy-paste examples
Whole scripts for the jobs that come up first. Each one compiles against com.microtube.hexr 0.9.4. Feeling an object while it is grabbed needs no code at all: put HexRInteractableHaptics on it.
Connect both gloves from code
No HexR menu needed. Put it on any object in the scene. In the Editor, connect from the HexR Haptics Tester instead — see Connecting the glove.
using UnityEngine;
using HaptGlove;
using HexR;
// Connects both gloves when the scene starts -- no HexR menu needed --
// and logs each one as it comes up or drops.
public class ConnectGloves : MonoBehaviour
{
void Start()
{
HexRManager rig = HexRManager.Instance;
rig.leftHand.onBluetoothConnected += OnConnected;
rig.rightHand.onBluetoothConnected += OnConnected;
rig.leftHand.onBluetoothDisconnected += OnDisconnected;
rig.rightHand.onBluetoothDisconnected += OnDisconnected;
// Does nothing for a glove that is already connected or connecting.
rig.Connect(HaptGloveHandler.HandType.Left);
rig.Connect(HaptGloveHandler.HandType.Right);
}
void OnDestroy()
{
HexRManager rig = HexRManager.Instance;
if (rig == null) return;
rig.leftHand.onBluetoothConnected -= OnConnected;
rig.rightHand.onBluetoothConnected -= OnConnected;
rig.leftHand.onBluetoothDisconnected -= OnDisconnected;
rig.rightHand.onBluetoothDisconnected -= OnDisconnected;
}
void OnConnected(HaptGloveHandler.HandType hand) { Debug.Log(hand + " glove connected"); }
void OnDisconnected(HaptGloveHandler.HandType hand) { Debug.Log(hand + " glove disconnected"); }
}Press a finger on your own event
Drag a Pressure Controller into Hand, then wire Press and Release to any UnityEvent.
using UnityEngine;
using HaptGlove;
using HexR;
// Wire Press() and Release() to any UnityEvent: a button, a trigger, an
// animation event. Strength is on the same 10-60 scale as the components'.
public class PressOnEvent : MonoBehaviour
{
public PressureTrackerMain hand; // a Pressure Controller
public Haptics.Finger finger = Haptics.Finger.Index;
[Range(10f, 60f)] public float strength = 30f;
bool pressing;
public void Press()
{
float intensity = PressureTrackerMain.StrengthToIntensity(strength);
// true skips the hand-near check: this press answers your event, not a touch.
hand.CustomSingleHaptics(finger, true, intensity, 1f, true);
pressing = true;
}
public void Release()
{
if (!pressing) return;
hand.CustomSingleHaptics(finger, false, 0f, 1f, true);
pressing = false;
}
// Disabled, or the scene changes, mid-press: let go as well.
void OnDisable() { Release(); }
}Press the finger that touches an object
On any object with a trigger collider. HapticFingerTrigger on the touch point says which hand and finger touched, so it needs no Pressure Controller of its own.
using System.Collections.Generic;
using UnityEngine;
using HexR;
// On a trigger collider: presses exactly the finger or palm that touches it,
// on the hand it belongs to, and releases it when it leaves.
[RequireComponent(typeof(Collider))]
public class PressWhereTouched : MonoBehaviour
{
[Range(0.1f, 1f)] public float intensity = 0.5f;
readonly HashSet<HapticFingerTrigger> touching = new HashSet<HapticFingerTrigger>();
void OnTriggerEnter(Collider other)
{
if (!other.TryGetComponent(out HapticFingerTrigger touch)) return;
touch.TriggerFixPressure(intensity); // 0.1-1, skips the hand-near check
touching.Add(touch);
}
void OnTriggerExit(Collider other)
{
if (!other.TryGetComponent(out HapticFingerTrigger touch)) return;
touch.RemoveHaptics();
touching.Remove(touch);
}
// Disabled while a finger is inside: release what is still pressed.
void OnDisable()
{
foreach (HapticFingerTrigger touch in touching)
{
if (touch != null) touch.RemoveHaptics();
}
touching.Clear();
}
}Release on reconnect
A glove whose link dropped is still pressing whatever it was told last. This releases it as soon as it is back.
using UnityEngine;
using HaptGlove;
using HexR;
// A glove whose Bluetooth link dropped keeps its last pressure. When it
// reconnects, release everything before anything else is sent.
public class ReleaseOnReconnect : MonoBehaviour
{
void Start()
{
HexRManager.Instance.leftHand.onBluetoothConnected += ReleaseAll;
HexRManager.Instance.rightHand.onBluetoothConnected += ReleaseAll;
}
void OnDestroy()
{
if (HexRManager.Instance == null) return;
HexRManager.Instance.leftHand.onBluetoothConnected -= ReleaseAll;
HexRManager.Instance.rightHand.onBluetoothConnected -= ReleaseAll;
}
void ReleaseAll(HaptGloveHandler.HandType hand)
{
HaptGloveHandler glove = hand == HaptGloveHandler.HandType.Left
? HexRManager.Instance.leftHand
: HexRManager.Instance.rightHand;
glove.ReleaseAllChannels();
}
}Where to go next
Connect(hand)HexRInteractableHaptics on an objectIntegrating into an existing app? Start with HexRInteractableHaptics before the lower-level PressureTrackerMain or HaptGloveHandler.