├── .gitignore
├── .jshintrc
├── LICENSE
├── README.md
├── browser.js
├── dist
├── aframe-gamepad-controls.js
└── aframe-gamepad-controls.min.js
├── examples
├── basic
│ └── index.html
├── index.html
└── scene
│ ├── assets
│ ├── 321103__nsstudios__blip1.wav
│ ├── peach-gradient-1.jpg
│ ├── tex
│ │ └── shadow-circle.png
│ ├── tree1.dae
│ └── tree2.dae
│ └── index.html
├── gamepad-controls.js
├── lib
├── GamepadButton.js
└── GamepadButtonEvent.js
├── package.json
└── tests
├── .jshintrc
├── __init.test.js
├── gamepad-controls.test.js
├── helpers.js
└── karma.conf.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .DS_Store
3 | .gh-pages
4 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "globals": {
3 | "THREE": false,
4 | "window": false,
5 | "document": false,
6 | "require": false,
7 | "console": false,
8 | "module": false
9 | },
10 | "bitwise": false,
11 | "browser": true,
12 | "eqeqeq": true,
13 | "esnext": true,
14 | "expr": true,
15 | "forin": true,
16 | "immed": true,
17 | "latedef": "nofunc",
18 | "laxbreak": true,
19 | "maxlen": 100,
20 | "newcap": true,
21 | "noarg": true,
22 | "noempty": true,
23 | "noyield": true,
24 | "quotmark": "single",
25 | "smarttabs": false,
26 | "trailing": true,
27 | "undef": true,
28 | "unused": true,
29 | "white": false
30 | }
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | The MIT License (MIT)
3 |
4 | Copyright (c) 2015 Don McCurdy
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # A-Frame `gamepad-controls`
2 |
3 | > **NOTICE**: This project is no longer maintained. I recommend using `movement-controls` from [A-Frame Extras](https://github.com/donmccurdy/aframe-extras) instead.
4 |
5 | Gamepad controls for A-Frame.
6 |
7 | Demo: https://donmccurdy.github.io/aframe-gamepad-controls/
8 |
9 | ## Overview
10 |
11 | Supports one or more gamepads, attached to an A-Frame scene. When used on a mobile device, `gamepad-controls` can also receive input from a gamepad connected to a host machine, using [ProxyControls.js](https://proxy-controls.donmccurdy.com).
12 |
13 | This component uses the HTML5 [Gamepad API](https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API). The underlying API supports Firefox, Chrome, Edge, and Opera ([as of 01/2016](http://caniuse.com/#search=gamepad)). Safari and Internet Explorer do not currently support gamepads.
14 |
15 | ## Usage (script)
16 |
17 | ```html
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | ```
34 |
35 | ## Usage (NPM)
36 |
37 | Install NPM module.
38 |
39 | ```
40 | $ npm install aframe-gamepad-controls
41 | ```
42 |
43 | Register `gamepad-controls` component.
44 |
45 | ```javascript
46 | var AFRAME = require('aframe');
47 | var GamepadControls = require('aframe-gamepad-controls');
48 | AFRAME.registerComponent('gamepad-controls', GamepadControls);
49 | ```
50 |
51 | Add markup.
52 |
53 | ```html
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | ```
64 |
65 | ## Development
66 |
67 | To edit the component or play with examples, [download the project](https://github.com/donmccurdy/aframe-gamepad-controls/archive/master.zip) and run:
68 |
69 | ```shell
70 | npm install
71 | npm run dev
72 | ```
73 |
74 | The demo will run at [http://localhost:8000/](http://localhost:8000/).
75 |
76 | ## Mobile / Cardboard + Gamepad
77 |
78 | In Chrome on Android, USB gamepads can be connected with an OTG adapter. For a Nexus 5X, I use [this](http://www.amazon.com/gp/product/B00XHOGEZG). I'm not aware of a way to connect a gamepad in iOS, but definitely let me know if there's something I'm missing.
79 |
80 | The `gamepad-controls` component can also receive remote events with WebRTC, if a `proxy-controls` element is attached to the scene. [More details about ProxyControls.js](https://proxy-controls.donmccurdy.com).
81 |
82 | Example:
83 |
84 | ```html
85 |
86 |
88 |
89 |
90 | ```
91 |
92 | ## Gear VR
93 |
94 | A-Frame supports the Gear VR controller with `gear-vr-controller`, but for Gear VR's without a controller, this component can be used to handle it's trackpad events.
95 |
96 | For a cursor-based setup, `downEvents` must be set to `gamepadbuttondown`. For example:
97 |
98 | ```html
99 |
100 | ```
101 |
102 | ## Button Events
103 |
104 | When buttons are pressed on the gamepad, a [GamepadButtonEvent](https://github.com/donmccurdy/aframe-gamepad-controls/blob/master/lib/GamepadButtonEvent.js) is emitted on the element. Components and entities may listen for these events and modify behavior as needed. Example:
105 |
106 | ```javascript
107 | el.addEventListener('gamepadbuttondown', function (e) {
108 | console.log('Button "%d" has been pressed.', e.index);
109 | });
110 | ```
111 |
112 | **GamepadButtonEvent:**
113 |
114 | Property | Type | Description
115 | ---------|---------|--------------
116 | type | string | Either `gamepadbuttondown` or `gamepadbuttonup`.
117 | index | int | Index of the button affected, 0..N.
118 | pressed | boolean | Whether or not the button is currently pressed.
119 | value | float | Distance the button was pressed, if applicable. Value will be 0 or 1 in most cases, but may return a float on the interval [0..1] for trigger-like buttons.
120 |
121 | **Markup-only Binding:**
122 |
123 | For convenience, additional events are fired including the button index, providing a way to bind events to specific buttons using only markup. To play `pew-pew.wav` when `Button 7` is pressed (right trigger on an Xbox controller), you might do this:
124 |
125 | ```html
126 |
129 |
130 | ```
131 |
132 | Finally, your code may call the `gamepad-controls` component directly to request the state of a button, as a [GamepadButton](https://developer.mozilla.org/en-US/docs/Web/API/GamepadButton) instance:
133 |
134 | ```javascript
135 | el.components['gamepad-controls'].getButton(index);
136 | // Returns a GamepadButton instance.
137 | ```
138 |
139 | ## Options
140 |
141 | Property | Default | Description
142 | ------------------|---------|-------------
143 | controller | 0 | Which controller (0..3) the object should be attached to.
144 | enabled | true | Enables all events on this controller.
145 | movementEnabled | true | Enables movement via the left thumbstick.
146 | lookEnabled | true | `true`, or `false`. Enables view rotation via the right thumbstick.
147 | flyEnabled | false | Whether or not movement is restricted to the entity’s initial plane.
148 | invertAxisY | false | Invert Y axis of view rotation thumbstick.
149 | debug | false | When true, shows debugging info in the console.
150 |
--------------------------------------------------------------------------------
/browser.js:
--------------------------------------------------------------------------------
1 |
2 | // Browser distrubution of the A-Frame component.
3 | (function (AFRAME) {
4 | if (!AFRAME) {
5 | console.error('Component attempted to register before AFRAME was available.');
6 | return;
7 | }
8 |
9 | (AFRAME.aframeCore || AFRAME).registerComponent('gamepad-controls', require('./gamepad-controls'));
10 |
11 | }(window.AFRAME));
12 |
--------------------------------------------------------------------------------
/dist/aframe-gamepad-controls.js:
--------------------------------------------------------------------------------
1 | /******/ (function(modules) { // webpackBootstrap
2 | /******/ // The module cache
3 | /******/ var installedModules = {};
4 |
5 | /******/ // The require function
6 | /******/ function __webpack_require__(moduleId) {
7 |
8 | /******/ // Check if module is in cache
9 | /******/ if(installedModules[moduleId])
10 | /******/ return installedModules[moduleId].exports;
11 |
12 | /******/ // Create a new module (and put it into the cache)
13 | /******/ var module = installedModules[moduleId] = {
14 | /******/ exports: {},
15 | /******/ id: moduleId,
16 | /******/ loaded: false
17 | /******/ };
18 |
19 | /******/ // Execute the module function
20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
21 |
22 | /******/ // Flag the module as loaded
23 | /******/ module.loaded = true;
24 |
25 | /******/ // Return the exports of the module
26 | /******/ return module.exports;
27 | /******/ }
28 |
29 |
30 | /******/ // expose the modules object (__webpack_modules__)
31 | /******/ __webpack_require__.m = modules;
32 |
33 | /******/ // expose the module cache
34 | /******/ __webpack_require__.c = installedModules;
35 |
36 | /******/ // __webpack_public_path__
37 | /******/ __webpack_require__.p = "";
38 |
39 | /******/ // Load entry module and return exports
40 | /******/ return __webpack_require__(0);
41 | /******/ })
42 | /************************************************************************/
43 | /******/ ([
44 | /* 0 */
45 | /***/ function(module, exports, __webpack_require__) {
46 |
47 |
48 | // Browser distrubution of the A-Frame component.
49 | (function (AFRAME) {
50 | if (!AFRAME) {
51 | console.error('Component attempted to register before AFRAME was available.');
52 | return;
53 | }
54 |
55 | (AFRAME.aframeCore || AFRAME).registerComponent('gamepad-controls', __webpack_require__(1));
56 |
57 | }(window.AFRAME));
58 |
59 |
60 | /***/ },
61 | /* 1 */
62 | /***/ function(module, exports, __webpack_require__) {
63 |
64 | /**
65 | * Gamepad controls for A-Frame.
66 | *
67 | * For more information about the Gamepad API, see:
68 | * https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API/Using_the_Gamepad_API
69 | */
70 |
71 | var GamepadButton = __webpack_require__(2),
72 | GamepadButtonEvent = __webpack_require__(3);
73 |
74 | var MAX_DELTA = 200, // ms
75 | PI_2 = Math.PI / 2;
76 |
77 | var JOYSTICK_EPS = 0.2;
78 |
79 | module.exports = {
80 |
81 | /*******************************************************************
82 | * Statics
83 | */
84 |
85 | GamepadButton: GamepadButton,
86 |
87 | /*******************************************************************
88 | * Schema
89 | */
90 |
91 | schema: {
92 | // Controller 0-3
93 | controller: { default: 0, oneOf: [0, 1, 2, 3] },
94 |
95 | // Enable/disable features
96 | enabled: { default: true },
97 | movementEnabled: { default: true },
98 | lookEnabled: { default: true },
99 | flyEnabled: { default: false },
100 | invertAxisY: { default: false },
101 |
102 | // Constants
103 | easing: { default: 20 },
104 | acceleration: { default: 65 },
105 | sensitivity: { default: 0.04 },
106 |
107 | // Control axes
108 | pitchAxis: { default: 'x', oneOf: [ 'x', 'y', 'z' ] },
109 | yawAxis: { default: 'y', oneOf: [ 'x', 'y', 'z' ] },
110 | rollAxis: { default: 'z', oneOf: [ 'x', 'y', 'z' ] },
111 |
112 | // Debugging
113 | debug: { default: false }
114 | },
115 |
116 | /*******************************************************************
117 | * Core
118 | */
119 |
120 | /**
121 | * Called once when component is attached. Generally for initial setup.
122 | */
123 | init: function () {
124 | // Movement
125 | this.velocity = new THREE.Vector3(0, 0, 0);
126 | this.direction = new THREE.Vector3(0, 0, 0);
127 |
128 | // Rotation
129 | this.pitch = new THREE.Object3D();
130 | this.yaw = new THREE.Object3D();
131 | this.yaw.position.y = 10;
132 | this.yaw.add(this.pitch);
133 |
134 | // Button state
135 | this.buttons = {};
136 |
137 | if (!this.getGamepad()) {
138 | console.warn(
139 | 'Gamepad #%d not found. Connect controller and press any button to continue.',
140 | this.data.controller
141 | );
142 | }
143 | },
144 |
145 | /**
146 | * Called on each iteration of main render loop.
147 | */
148 | tick: function (t, dt) {
149 | this.updateRotation(dt);
150 | this.updatePosition(dt);
151 | this.updateButtonState();
152 | },
153 |
154 | /*******************************************************************
155 | * Movement
156 | */
157 |
158 | updatePosition: function (dt) {
159 | var data = this.data;
160 | var acceleration = data.acceleration;
161 | var easing = data.easing;
162 | var velocity = this.velocity;
163 | var rollAxis = data.rollAxis;
164 | var pitchAxis = data.pitchAxis;
165 | var el = this.el;
166 | var gamepad = this.getGamepad();
167 |
168 | // If data has changed or FPS is too low
169 | // we reset the velocity
170 | if (dt > MAX_DELTA) {
171 | velocity[rollAxis] = 0;
172 | velocity[pitchAxis] = 0;
173 | return;
174 | }
175 |
176 | velocity[rollAxis] -= velocity[rollAxis] * easing * dt / 1000;
177 | velocity[pitchAxis] -= velocity[pitchAxis] * easing * dt / 1000;
178 |
179 | var position = el.getAttribute('position');
180 |
181 | if (data.enabled && data.movementEnabled && gamepad) {
182 | var dpad = this.getDpad(),
183 | inputX = dpad.x || this.getJoystick(0).x,
184 | inputY = dpad.y || this.getJoystick(0).y;
185 | if (Math.abs(inputX) > JOYSTICK_EPS) {
186 | velocity[pitchAxis] += inputX * acceleration * dt / 1000;
187 | }
188 | if (Math.abs(inputY) > JOYSTICK_EPS) {
189 | velocity[rollAxis] += inputY * acceleration * dt / 1000;
190 | }
191 | }
192 |
193 | var movementVector = this.getMovementVector(dt);
194 |
195 | el.object3D.translateX(movementVector.x);
196 | el.object3D.translateY(movementVector.y);
197 | el.object3D.translateZ(movementVector.z);
198 |
199 | el.setAttribute('position', {
200 | x: position.x + movementVector.x,
201 | y: position.y + movementVector.y,
202 | z: position.z + movementVector.z
203 | });
204 | },
205 |
206 | getMovementVector: function (dt) {
207 | if (this._getMovementVector) {
208 | return this._getMovementVector(dt);
209 | }
210 |
211 | var euler = new THREE.Euler(0, 0, 0, 'YXZ'),
212 | rotation = new THREE.Vector3();
213 |
214 | this._getMovementVector = function (dt) {
215 | rotation.copy(this.el.getAttribute('rotation'));
216 | this.direction.copy(this.velocity);
217 | this.direction.multiplyScalar(dt / 1000);
218 | if (!rotation) { return this.direction; }
219 | if (!this.data.flyEnabled) { rotation.x = 0; }
220 | euler.set(
221 | THREE.Math.degToRad(rotation.x),
222 | THREE.Math.degToRad(rotation.y),
223 | 0
224 | );
225 | this.direction.applyEuler(euler);
226 | return this.direction;
227 | };
228 |
229 | return this._getMovementVector(dt);
230 | },
231 |
232 | /*******************************************************************
233 | * Rotation
234 | */
235 |
236 | updateRotation: function () {
237 | if (this._updateRotation) {
238 | return this._updateRotation();
239 | }
240 |
241 | var initialRotation = new THREE.Vector3(),
242 | prevInitialRotation = new THREE.Vector3(),
243 | prevFinalRotation = new THREE.Vector3();
244 |
245 | var tCurrent,
246 | tLastLocalActivity = 0,
247 | tLastExternalActivity = 0;
248 |
249 | var ROTATION_EPS = 0.0001,
250 | DEBOUNCE = 500;
251 |
252 | this._updateRotation = function () {
253 | if (!this.data.lookEnabled || !this.getGamepad()) {
254 | return;
255 | }
256 |
257 | tCurrent = Date.now();
258 | initialRotation.copy(this.el.getAttribute('rotation') || initialRotation);
259 |
260 | // If initial rotation for this frame is different from last frame, and
261 | // doesn't match last gamepad state, assume an external component is
262 | // active on this element.
263 | if (initialRotation.distanceToSquared(prevInitialRotation) > ROTATION_EPS
264 | && initialRotation.distanceToSquared(prevFinalRotation) > ROTATION_EPS) {
265 | prevInitialRotation.copy(initialRotation);
266 | tLastExternalActivity = tCurrent;
267 | return;
268 | }
269 |
270 | prevInitialRotation.copy(initialRotation);
271 |
272 | // If external controls have been active in last 500ms, wait.
273 | if (tCurrent - tLastExternalActivity < DEBOUNCE) {
274 | return;
275 | }
276 |
277 | var lookVector = this.getJoystick(1);
278 | if (Math.abs(lookVector.x) <= JOYSTICK_EPS) lookVector.x = 0;
279 | if (Math.abs(lookVector.y) <= JOYSTICK_EPS) lookVector.y = 0;
280 | if (this.data.invertAxisY) lookVector.y = -lookVector.y;
281 |
282 | // If external controls have been active more recently than gamepad,
283 | // and gamepad hasn't moved, don't overwrite the existing rotation.
284 | if (tLastExternalActivity > tLastLocalActivity && !lookVector.lengthSq()) {
285 | return;
286 | }
287 |
288 | lookVector.multiplyScalar(this.data.sensitivity);
289 | this.yaw.rotation.y -= lookVector.x;
290 | this.pitch.rotation.x -= lookVector.y;
291 | this.pitch.rotation.x = Math.max(-PI_2, Math.min(PI_2, this.pitch.rotation.x));
292 |
293 | this.el.setAttribute('rotation', {
294 | x: THREE.Math.radToDeg(this.pitch.rotation.x),
295 | y: THREE.Math.radToDeg(this.yaw.rotation.y),
296 | z: 0
297 | });
298 | prevFinalRotation.copy(this.el.getAttribute('rotation'));
299 | tLastLocalActivity = tCurrent;
300 | };
301 |
302 | return this._updateRotation();
303 | },
304 |
305 | /*******************************************************************
306 | * Button events
307 | */
308 |
309 | updateButtonState: function () {
310 | var gamepad = this.getGamepad();
311 | if (this.data.enabled && gamepad) {
312 |
313 | // Fire DOM events for button state changes.
314 | for (var i = 0; i < gamepad.buttons.length; i++) {
315 | if (gamepad.buttons[i].pressed && !this.buttons[i]) {
316 | this.emit(new GamepadButtonEvent('gamepadbuttondown', i, gamepad.buttons[i]));
317 | } else if (!gamepad.buttons[i].pressed && this.buttons[i]) {
318 | this.emit(new GamepadButtonEvent('gamepadbuttonup', i, gamepad.buttons[i]));
319 | }
320 | this.buttons[i] = gamepad.buttons[i].pressed;
321 | }
322 |
323 | } else if (Object.keys(this.buttons)) {
324 | // Reset state if controls are disabled or controller is lost.
325 | this.buttons = {};
326 | }
327 | },
328 |
329 | emit: function (event) {
330 | // Emit original event.
331 | this.el.emit(event.type, event);
332 |
333 | // Emit convenience event, identifying button index.
334 | this.el.emit(
335 | event.type + ':' + event.index,
336 | new GamepadButtonEvent(event.type, event.index, event)
337 | );
338 | },
339 |
340 | /*******************************************************************
341 | * Gamepad state
342 | */
343 |
344 | /**
345 | * Returns the Gamepad instance attached to the component. If connected,
346 | * a proxy-controls component may provide access to Gamepad input from a
347 | * remote device.
348 | *
349 | * @return {Gamepad}
350 | */
351 | getGamepad: function () {
352 | var localGamepad = navigator.getGamepads
353 | && navigator.getGamepads()[this.data.controller],
354 | proxyControls = this.el.sceneEl.components['proxy-controls'],
355 | proxyGamepad = proxyControls && proxyControls.isConnected()
356 | && proxyControls.getGamepad(this.data.controller);
357 | return proxyGamepad || localGamepad;
358 | },
359 |
360 | /**
361 | * Returns the state of the given button.
362 | * @param {number} index The button (0-N) for which to find state.
363 | * @return {GamepadButton}
364 | */
365 | getButton: function (index) {
366 | return this.getGamepad().buttons[index];
367 | },
368 |
369 | /**
370 | * Returns state of the given axis. Axes are labelled 0-N, where 0-1 will
371 | * represent X/Y on the first joystick, and 2-3 X/Y on the second.
372 | * @param {number} index The axis (0-N) for which to find state.
373 | * @return {number} On the interval [-1,1].
374 | */
375 | getAxis: function (index) {
376 | return this.getGamepad().axes[index];
377 | },
378 |
379 | /**
380 | * Returns the state of the given joystick (0 or 1) as a THREE.Vector2.
381 | * @param {number} id The joystick (0, 1) for which to find state.
382 | * @return {THREE.Vector2}
383 | */
384 | getJoystick: function (index) {
385 | var gamepad = this.getGamepad();
386 | switch (index) {
387 | case 0: return new THREE.Vector2(gamepad.axes[0], gamepad.axes[1]);
388 | case 1: return new THREE.Vector2(gamepad.axes[2], gamepad.axes[3]);
389 | default: throw new Error('Unexpected joystick index "%d".', index);
390 | }
391 | },
392 |
393 | /**
394 | * Returns the state of the dpad as a THREE.Vector2.
395 | * @return {THREE.Vector2}
396 | */
397 | getDpad: function () {
398 | var gamepad = this.getGamepad();
399 | if (!gamepad.buttons[GamepadButton.DPAD_RIGHT]) {
400 | return new THREE.Vector2();
401 | }
402 | return new THREE.Vector2(
403 | (gamepad.buttons[GamepadButton.DPAD_RIGHT].pressed ? 1 : 0)
404 | + (gamepad.buttons[GamepadButton.DPAD_LEFT].pressed ? -1 : 0),
405 | (gamepad.buttons[GamepadButton.DPAD_UP].pressed ? -1 : 0)
406 | + (gamepad.buttons[GamepadButton.DPAD_DOWN].pressed ? 1 : 0)
407 | );
408 | },
409 |
410 | /**
411 | * Returns true if the gamepad is currently connected to the system.
412 | * @return {boolean}
413 | */
414 | isConnected: function () {
415 | var gamepad = this.getGamepad();
416 | return !!(gamepad && gamepad.connected);
417 | },
418 |
419 | /**
420 | * Returns a string containing some information about the controller. Result
421 | * may vary across browsers, for a given controller.
422 | * @return {string}
423 | */
424 | getID: function () {
425 | return this.getGamepad().id;
426 | }
427 | };
428 |
429 |
430 | /***/ },
431 | /* 2 */
432 | /***/ function(module, exports) {
433 |
434 | module.exports = Object.assign(function GamepadButton () {}, {
435 | FACE_1: 0,
436 | FACE_2: 1,
437 | FACE_3: 2,
438 | FACE_4: 3,
439 |
440 | L_SHOULDER_1: 4,
441 | R_SHOULDER_1: 5,
442 | L_SHOULDER_2: 6,
443 | R_SHOULDER_2: 7,
444 |
445 | SELECT: 8,
446 | START: 9,
447 |
448 | DPAD_UP: 12,
449 | DPAD_DOWN: 13,
450 | DPAD_LEFT: 14,
451 | DPAD_RIGHT: 15,
452 |
453 | VENDOR: 16,
454 | });
455 |
456 |
457 | /***/ },
458 | /* 3 */
459 | /***/ function(module, exports) {
460 |
461 | function GamepadButtonEvent (type, index, details) {
462 | this.type = type;
463 | this.index = index;
464 | this.pressed = details.pressed;
465 | this.value = details.value;
466 | }
467 |
468 | module.exports = GamepadButtonEvent;
469 |
470 |
471 | /***/ }
472 | /******/ ]);
--------------------------------------------------------------------------------
/dist/aframe-gamepad-controls.min.js:
--------------------------------------------------------------------------------
1 | !function(t){function e(n){if(i[n])return i[n].exports;var o=i[n]={exports:{},id:n,loaded:!1};return t[n].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var i={};return e.m=t,e.c=i,e.p="",e(0)}([function(t,e,i){!function(t){return t?void(t.aframeCore||t).registerComponent("gamepad-controls",i(1)):void console.error("Component attempted to register before AFRAME was available.")}(window.AFRAME)},function(t,e,i){var n=i(2),o=i(3),a=200,s=Math.PI/2,r=.2;t.exports={GamepadButton:n,schema:{controller:{"default":0,oneOf:[0,1,2,3]},enabled:{"default":!0},movementEnabled:{"default":!0},lookEnabled:{"default":!0},flyEnabled:{"default":!1},invertAxisY:{"default":!1},easing:{"default":20},acceleration:{"default":65},sensitivity:{"default":.04},pitchAxis:{"default":"x",oneOf:["x","y","z"]},yawAxis:{"default":"y",oneOf:["x","y","z"]},rollAxis:{"default":"z",oneOf:["x","y","z"]},debug:{"default":!1}},init:function(){this.velocity=new THREE.Vector3(0,0,0),this.direction=new THREE.Vector3(0,0,0),this.pitch=new THREE.Object3D,this.yaw=new THREE.Object3D,this.yaw.position.y=10,this.yaw.add(this.pitch),this.buttons={},this.getGamepad()||console.warn("Gamepad #%d not found. Connect controller and press any button to continue.",this.data.controller)},tick:function(t,e){this.updateRotation(e),this.updatePosition(e),this.updateButtonState()},updatePosition:function(t){var e=this.data,i=e.acceleration,n=e.easing,o=this.velocity,s=e.rollAxis,d=e.pitchAxis,u=this.el,c=this.getGamepad();if(t>a)return o[s]=0,void(o[d]=0);o[s]-=o[s]*n*t/1e3,o[d]-=o[d]*n*t/1e3;var h=u.getAttribute("position");if(e.enabled&&e.movementEnabled&&c){var l=this.getDpad(),p=l.x||this.getJoystick(0).x,E=l.y||this.getJoystick(0).y;Math.abs(p)>r&&(o[d]+=p*i*t/1e3),Math.abs(E)>r&&(o[s]+=E*i*t/1e3)}var f=this.getMovementVector(t);u.object3D.translateX(f.x),u.object3D.translateY(f.y),u.object3D.translateZ(f.z),u.setAttribute("position",{x:h.x+f.x,y:h.y+f.y,z:h.z+f.z})},getMovementVector:function(t){if(this._getMovementVector)return this._getMovementVector(t);var e=new THREE.Euler(0,0,0,"YXZ"),i=new THREE.Vector3;return this._getMovementVector=function(t){return i.copy(this.el.getAttribute("rotation")),this.direction.copy(this.velocity),this.direction.multiplyScalar(t/1e3),i?(this.data.flyEnabled||(i.x=0),e.set(THREE.Math.degToRad(i.x),THREE.Math.degToRad(i.y),0),this.direction.applyEuler(e),this.direction):this.direction},this._getMovementVector(t)},updateRotation:function(){if(this._updateRotation)return this._updateRotation();var t,e=new THREE.Vector3,i=new THREE.Vector3,n=new THREE.Vector3,o=0,a=0,d=1e-4,u=500;return this._updateRotation=function(){if(this.data.lookEnabled&&this.getGamepad()){if(t=Date.now(),e.copy(this.el.getAttribute("rotation")||e),e.distanceToSquared(i)>d&&e.distanceToSquared(n)>d)return i.copy(e),void(a=t);if(i.copy(e),!(u>t-a)){var c=this.getJoystick(1);Math.abs(c.x)<=r&&(c.x=0),Math.abs(c.y)<=r&&(c.y=0),this.data.invertAxisY&&(c.y=-c.y),a>o&&!c.lengthSq()||(c.multiplyScalar(this.data.sensitivity),this.yaw.rotation.y-=c.x,this.pitch.rotation.x-=c.y,this.pitch.rotation.x=Math.max(-s,Math.min(s,this.pitch.rotation.x)),this.el.setAttribute("rotation",{x:THREE.Math.radToDeg(this.pitch.rotation.x),y:THREE.Math.radToDeg(this.yaw.rotation.y),z:0}),n.copy(this.el.getAttribute("rotation")),o=t)}}},this._updateRotation()},updateButtonState:function(){var t=this.getGamepad();if(this.data.enabled&&t)for(var e=0;e
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
15 |