In XR games that are played on VR googles, it would be nice to use also haptic feedback and get callback on button presses.
Is there an interface to this functionality? I saw a pull request but could not find information on how to e.g. activate the vibration of the grabbing hand or how to get alerted to button presses.
@RaananW, can you help with using these features?
You can use heptic feedback using the API provided on the controller - WebXRAbstractMotionController | Babylon.js Documentation (babylonjs.com) this will either work (if your controller supports it) or will fail silently.
Thanks! This is great. Is there an example playground?
Is it correct to assume that you do not need to create the actual motion controller object, but access it via the input to the callback function:
âxr.input.onControllerAddedObservable.addâ
as shown in this playgrounds:
Are there other playgrounds to look at?
One bad news is that the WebXR emulator under Windows 10 does not seem to present itself as a âgame controllerâ even though it has all the neccessary gizmos. See this code:
xr.input.onControllerAddedObservable.add((controller => {
console.log(`Controller is: ${controller}`);
console.log(`Controller added: ${controller.gamepadController}`);
if (controller.gamepadController) {
console.log(`Controller added. Profile: ${controller.gamepadController.profileId}`);
for (var key in controller.gamepadController.components) {
controller.gamepadController.components[key].onButtonStateChanged.add(() => {
console.log(`Button ${key} value changed`);
});
controller.gamepadController.components[key].onAxisValueChanged.add(() => {
console.log(`Axis ${key} value changed`);
});
}
}
}));
Same with the aforementioned workspace.
Yes, this is the right way to deal with that. There are others (if you know itâs already constructed you can get it dorectly from the WebXR Input class), but this is the better one. this doc page discusses the process: WebXR Controllers Support | Babylon.js Documentation
Are you referring to the chrome extension? or to the microsoft mixed reality app with emulated headset?
To be honest, I donât really know, to be honest. But I guess that it is the microsoft WebXR tool. I set it to âQuest 2â, yet controller.gamepadController is already âundefinedâ in the code above. When running it on my Pico 4, I also got no response, but since its hard to debug on the device, I cannot for sure pin down the reason. Yet most probably it is also no âgamepadControllerâ being present.
TThis is probably not the microsoft tool, as you canât set it to be quest 2. microsoft have different device in their emulator.
The chrome-based emulator does generate a gamepad object:
So I am not sure what emulator you are using
Thanks. This was very helpful. Apparently I was using completely wrong vocabulary here.
xr.input.controllers[0].inputSource.gamepad.buttons[0].pressed
works nicely and I can monitor state changes. The code above can be modified to
xr.input.onControllerAddedObservable.add((controller => {
console.log(`Controller is: ${controller}`);
console.log(`Controller InputSource is: ${controller.inputSource}`);
console.log(`Controller added: ${controller.inputSource.gamepad}`);
console.log(`Controller buttons: ${controller.inputSource.gamepad.buttons}`);
console.log(`Controller haptic: ${controller.inputSource.gamepad.hapticActuators}`);
var gamepadController = controller.inputSource.gamepad;
// Object.keys(controller.inputSource).forEach((prop)=> console.log(prop));
Object.keys(controller.inputSource.gamepad).forEach((prop)=> console.log(prop));
if (gamepadController != undefined) {
console.log(`Controller added. Profile ID: ${gamepadController.profileId}`);
n=0;
but then the following does NOT work:
for (var button in gamepadController.buttons) { // components
It looks like I somehow hooked into the wrong interface here and the observable is not an observable. By the way, how did you get such a nice interface to the various properties? In the inspector of the playground, I cannot see these.
Babylon masks this entire interface for you, so you donât have to deal with the different buttons and axes in the gamepad object. This is documented here - WebXR Controllers Support | Babylon.js Documentation (babylonjs.com)
I am not sure exactly what you are trying to achieve TBH. Want to share a playground so I can have some better context?
This is perfect. The example playground in there
works like a charm. Only the vibrate part in missing, but I can probably figure that one out somehow.
For anyone looking for how to do this, the pulse function is available on the controllerâs motionController.
// Haptic feedback when the trigger is pressed
xr.input.onControllerAddedObservable.add(controller => {
controller.onMotionControllerInitObservable.add(motionController => {
// motionController can be accessed through controller.motionController
// We need a state for tracking if the trigger was pressed,
// as the onButtonStateChangedObservable triggers every frame,
// while the trigger is lightly pressed
let wasPressed = false;
motionController.getComponent("xr-standard-trigger").onButtonStateChangedObservable.add(component => {
if (component.pressed && !wasPressed) {
motionController.pulse(0.05, 100);
}
wasPressed = component.pressed;
});
});
});
Itâs worth noting that the pulse function will override a previous call, even if the previous call had a longer duration or a higher strength.
So, if you have multiple classes that applies haptic feedback. You can use timeout, to ensure that haptic feedback that has a higher priority is always applied.
// Sends haptic feedback one frame later
setTimeout(() => motionController.pulse(0.05, 150));