A-Frame VR Controller Support (Quest2) Tutorial

A-Frame has native support for VR controller: https://aframe.io/docs/1.4.0/introduction/interactions-and-controllers.html

However, native support does not give you customization ability. Oculus Quest2 Controller also reported not working with the default controller component; to customize support for Oculus Quest2, here is a script I made based on https://github.com/gftruj/webzamples/tree/master/aframe ; this script will give you support to move around VR scene with thumbstick and head direction:


AFRAME.registerComponent('oculus-thumbstick-controls', {



   schema: {


       acceleration: { default: 45 },


       rigSelector: {default: "#rig"},


       fly: { default: false },


       controllerOriented: { default: false },


       adAxis: {default: 'x', oneOf: ['x', 'y', 'z']},


       wsAxis: {default: 'z', oneOf: ['x', 'y', 'z']},


       enabled: {default: true},


       adEnabled: {default: true},


       adInverted: {default: false},


       wsEnabled: {default: true},


       wsInverted: {default: false}


   },


   init: function () {


       this.easing = 1.1;


       this.velocity = new THREE.Vector3(0, 0, 0);


       this.tsData = new THREE.Vector2(0, 0);





       this.thumbstickMoved = this.thumbstickMoved.bind(this)


       this.el.addEventListener('thumbstickmoved', this.thumbstickMoved);


   },


   update: function() {


       this.rigElement = document.querySelector(this.data.rigSelector)


   },


   tick: function (time, delta) {


       if (!this.el.sceneEl.is('vr-mode')) return;


       var data = this.data;


       var el = this.rigElement


       var velocity = this.velocity;


       //console.log("here", this.tsData, this.tsData.length())


       if (!velocity[data.adAxis] && !velocity[data.wsAxis] && !this.tsData.length()) { return; }





       // Update velocity.


       delta = delta / 1000;


       this.updateVelocity(delta);





       if (!velocity[data.adAxis] && !velocity[data.wsAxis]) { return; }





       // Get movement vector and translate position.


       el.object3D.position.add(this.getMovementVector(delta));


   },


   updateVelocity: function (delta) {


       var acceleration;


       var adAxis;


       var adSign;


       var data = this.data;


       var velocity = this.velocity;


       var wsAxis;


       var wsSign;


       const CLAMP_VELOCITY = 0.00001;





       adAxis = data.adAxis;


       wsAxis = data.wsAxis;





       // If FPS too low, reset velocity.


       if (delta > 0.2) {


           velocity[adAxis] = 0;


           velocity[wsAxis] = 0;


           return;


       }





       // https://gamedev.stackexchange.com/questions/151383/frame-rate-independant-movement-with-acceleration


       var scaledEasing = Math.pow(1 / this.easing, delta * 60);


       // Velocity Easing.


       if (velocity[adAxis] !== 0) {


           velocity[adAxis] = velocity[adAxis] * scaledEasing;


       }


       if (velocity[wsAxis] !== 0) {


           velocity[wsAxis] = velocity[wsAxis] * scaledEasing;


       }





       // Clamp velocity easing.


       if (Math.abs(velocity[adAxis]) < CLAMP_VELOCITY) { velocity[adAxis] = 0; }


       if (Math.abs(velocity[wsAxis]) < CLAMP_VELOCITY) { velocity[wsAxis] = 0; }





       if (!data.enabled) { return; }





       // Update velocity using keys pressed.


       acceleration = data.acceleration;


       if (data.adEnabled && this.tsData.x) {


           adSign = data.adInverted ? -1 : 1;


           velocity[adAxis] += adSign * acceleration * this.tsData.x * delta;


       }


       if (data.wsEnabled) {


           wsSign = data.wsInverted ? -1 : 1;


           velocity[wsAxis] += wsSign * acceleration * this.tsData.y * delta;


       }


   },


   getMovementVector: (function () {


       var directionVector = new THREE.Vector3(0, 0, 0);


       var rotationEuler = new THREE.Euler(0, 0, 0, 'YXZ');





       return function (delta) {


           var rotation = this.el.sceneEl.camera.el.object3D.rotation


           var velocity = this.velocity;


           var xRotation;





           directionVector.copy(velocity);


           directionVector.multiplyScalar(delta);


           // Absolute.


           if (!rotation) { return directionVector; }


           xRotation = this.data.fly ? rotation.x : 0;





           // Transform direction relative to heading.


           rotationEuler.set(xRotation, rotation.y, 0);


           directionVector.applyEuler(rotationEuler);


           return directionVector;


       };


   })(),


   thumbstickMoved: function (evt) {


       this.tsData.set(evt.detail.x, evt.detail.y);


   },


   remove: function () {


       this.el.removeEventListener('thumbstickmoved', this.thumbstickMoved);


   }

Basic usage

<script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script>

<script src="https://gftruj.github.io/webzamples/aframe/controls/oculus-thumbstick-controls.js"></script>

<a-scene>

    <!-- Camera + controllers rig -->

    <a-entity id="rig">

        <a-camera position="0 1.6 0"></a-camera>

        <a-entity oculus-touch-controls="hand: left" ></a-entity>

        <a-entity oculus-touch-controls="hand: right" oculus-thumbstick-controls></a-entity>

    </a-entity>

</a-scene>

Properties