Drag Demystified —Multiple Drag Sliders in Threejs and AltspaceVR

William Bratches
Virtual Reality Development
2 min readMar 15, 2016

--

TL:DR:

  • Drag events should in initiated in the altspaceVR ‘ready’ callback
  • Every threejs object must be instantiated separately.

In the age of digital labor, we relish the opportunity for VR to give us what we truly want: a giant board of mechanically interactive doodads, buttons, and general slidy-thingys. Or at least in my case, a blocky emulation of of an analog synthesizer in AltspaceVR’s javascript-based app engine.

While attempting to build my synthesizer last week, I ran into a roadblock while realizing my dreams of tactile interaction: sparse documentation for drag and interaction events. Every time a cursor event was called on a threeJS cube that I wanted to be a movable slider, I got a cryptic error in my debugger:

Cannot read 'clientId' of undefined

My implementation of the drag slider was fired from the hip, and was in an isolated module that was not part of AltspaceVR’s initialization flow.

function createDraggableSlider() {
// make a cube with threeJS
var cube = Modules.ShapeMaker.createCube();
cube.addBehaviors(
alt.Object3DSync(),
alt.Drag({x: {min: 0, max: 100}, y: true})
);
}

The problem was simple: my function was being called from the wrong place. To get altspace-related behaviors working correctly, they must be part of sceneSync’s ‘ready’ callback.

// initialization after altspace.utilities.sync.connect
altspace.utilities.behaviors.SceneSync(connection.instance,
{
instantiators: {
‘createDraggableSlider’: createDraggableSlider, //from above
},
ready: ready
});
function ready(firstInstance) {
if (firstInstance) {
sceneSync.instantiate(‘createDraggableSlider’);
}
}

What if we want multiple draggable objects? We can use a simple for loop to initialize multiple sliders. The instantiation function must be called for every new object want to add behaviors for.

function ready(firstInstance) {
if (firstInstance) {
for (i=0; i < 5; i++) {
sceneSync.instantiate('createSlider');
}
}
}

Wait! We’ve only succeeded in creating multiple sliders that completely overlap each other, occupying the same exact location in space. To remedy this, we just make clever use of the for loop index. Let’s rewrite our createDraggableSlider function:

function createDraggableSlider(xIndex) { // new argument
// make a cube with threeJS and translate it
var cube = Modules.ShapeMaker.createCube();
var moveAmount = xIndex || 0;
cube.translateX(moveAmount)
cube.addBehaviors(
alt.Object3DSync(),
// use xIndex to determine our slider range
alt.Drag({x: {min: xIndex, max: xIndex + 100}, y: true}),
);
}

And we modify our ready function accordingly:

function ready(firstInstance) {
if (firstInstance) {
for (i=0; i < 5; i++) {
sceneSync.instantiate('createSlider', i * 500); // add spacing
}
}
}

There we have it - unlimited draggable objects in your altspaceVR app. Questions? Let me know in the comments!

--

--