diff --git a/v3/src/input/Pointer.js b/v3/src/input/Pointer.js index 380a86753..fa6788e66 100644 --- a/v3/src/input/Pointer.js +++ b/v3/src/input/Pointer.js @@ -65,6 +65,18 @@ var Pointer = new Class({ this.justDown = false; this.justUp = false; this.justMoved = false; + + /** + * @property {number} movementX - If the mouse is locked, the horizontal relative movement + * of the Pointer in pixels since last frame. + */ + this.movementX = 0; + + /** + * @property {number} movementY - If the mouse is locked, the vertical relative movement of + * the Pointer in pixels since last frame. + */ + this.movementY = 0; }, positionToCamera: function (camera, output) @@ -109,6 +121,8 @@ var Pointer = new Class({ this.justDown = false; this.justUp = false; this.justMoved = false; + this.movementX = 0; + this.movementY = 0; }, touchmove: function (event, time) @@ -135,6 +149,13 @@ var Pointer = new Class({ this.x = this.manager.transformX(event.pageX); this.y = this.manager.transformY(event.pageY); + if (this.manager.mouse.locked) + { + // Potentially multiple DOM events within one frame, but only one Phaser event will fire + this.movementX += event.movementX || event.mozMovementX || event.webkitMovementX || 0; + this.movementY += event.movementY || event.mozMovementY || event.webkitMovementY || 0; + } + this.justMoved = true; this.dirty = true; diff --git a/v3/src/input/global/GlobalInputManager.js b/v3/src/input/global/GlobalInputManager.js index 3239399cb..06618b4c3 100644 --- a/v3/src/input/global/GlobalInputManager.js +++ b/v3/src/input/global/GlobalInputManager.js @@ -113,6 +113,11 @@ var GlobalInputManager = new Class({ this.events.dispatch(new MouseEvent.UP(event)); break; + case 'pointerlockchange': + + this.events.dispatch(new MouseEvent.POINTER_LOCK_CHANGE(event, this.mouse.locked)); + break; + case 'touchmove': pointer.touchmove(event, time); diff --git a/v3/src/input/mouse/MouseManager.js b/v3/src/input/mouse/MouseManager.js index 11f46f907..bf5dd614c 100644 --- a/v3/src/input/mouse/MouseManager.js +++ b/v3/src/input/mouse/MouseManager.js @@ -1,4 +1,6 @@ var Class = require('../../utils/Class'); +var Features = require('../../device/Features'); +var MouseEvents = require('./events'); // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent // https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md @@ -19,6 +21,12 @@ var MouseManager = new Class({ this.target; this.handler; + + /** + * @property {boolean} locked - If the mouse has been pointer locked successfully this will + * be set to true. + */ + this.locked = false; }, boot: function () @@ -55,6 +63,52 @@ var MouseManager = new Class({ return this; }, + /** + * If the browser supports it, you can request that the pointer be locked to the browser window. + * This is classically known as 'FPS controls', where the pointer can't leave the browser until + * the user presses an exit key. If the browser successfully enters a locked state, a + * 'POINTER_LOCK_CHANGE_EVENT' will be dispatched - from the game's input manager - with an + * `isPointerLocked` property. + * It is important to note that pointer lock can only be enabled after an 'engagement gesture', + * see: https://w3c.github.io/pointerlock/#dfn-engagement-gesture. + */ + requestPointerLock: function () + { + if (Features.pointerLock) + { + var element = this.target; + element.requestPointerLock = element.requestPointerLock || element.mozRequestPointerLock || element.webkitRequestPointerLock; + element.requestPointerLock(); + } + }, + + /** + * Internal pointerLockChange handler. + * + * @param {Event} event - The native event from the browser. + */ + pointerLockChange: function (event) + { + var element = this.target; + this.locked = document.pointerLockElement === element || document.mozPointerLockElement === element || document.webkitPointerLockElement === element + ? true : false; + this.manager.queue.push(event); + }, + + /** + * If the browser supports pointer lock, this will request that the pointer lock is released. If + * the browser successfully enters a locked state, a 'POINTER_LOCK_CHANGE_EVENT' will be + * dispatched - from the game's input manager - with an `isPointerLocked` property. + */ + releasePointerLock: function () + { + if (Features.pointerLock) + { + document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock || document.webkitExitPointerLock; + document.exitPointerLock(); + } + }, + startListeners: function () { var queue = this.manager.queue; @@ -82,6 +136,14 @@ var MouseManager = new Class({ this.target.addEventListener('mousemove', handler, false); this.target.addEventListener('mousedown', handler, false); this.target.addEventListener('mouseup', handler, false); + + if (Features.pointerLock) + { + this.pointerLockChange = this.pointerLockChange.bind(this); + document.addEventListener('pointerlockchange', this.pointerLockChange, true); + document.addEventListener('mozpointerlockchange', this.pointerLockChange, true); + document.addEventListener('webkitpointerlockchange', this.pointerLockChange, true); + } }, stopListeners: function () @@ -89,6 +151,13 @@ var MouseManager = new Class({ this.target.removeEventListener('mousemove', this.handler); this.target.removeEventListener('mousedown', this.handler); this.target.removeEventListener('mouseup', this.handler); + + if (Features.pointerLock) + { + document.removeEventListener('pointerlockchange', this.pointerLockChange, true); + document.removeEventListener('mozpointerlockchange', this.pointerLockChange, true); + document.removeEventListener('webkitpointerlockchange', this.pointerLockChange, true); + } } }); diff --git a/v3/src/input/mouse/events/PointerLockChangeEvent.js b/v3/src/input/mouse/events/PointerLockChangeEvent.js new file mode 100644 index 000000000..eef9aa82a --- /dev/null +++ b/v3/src/input/mouse/events/PointerLockChangeEvent.js @@ -0,0 +1,20 @@ +var Class = require('../../../utils/Class'); +var Event = require('../../../events/Event'); + +var PointerLockChangeEvent = new Class({ + + Extends: Event, + + initialize: + + function PointerLockChangeEvent (nativeEvent, isPointerLocked) + { + Event.call(this, 'POINTER_LOCK_CHANGE_EVENT'); + + this.data = nativeEvent; + this.isPointerLocked = isPointerLocked; + } + +}); + +module.exports = PointerLockChangeEvent; diff --git a/v3/src/input/mouse/events/index.js b/v3/src/input/mouse/events/index.js index caf8a8fcc..cf597e0d7 100644 --- a/v3/src/input/mouse/events/index.js +++ b/v3/src/input/mouse/events/index.js @@ -3,5 +3,6 @@ module.exports = { DOWN: require('./MouseDownEvent'), UP: require('./MouseUpEvent'), - MOVE: require('./MouseMoveEvent') + MOVE: require('./MouseMoveEvent'), + POINTER_LOCK_CHANGE: require('./PointerLockChangeEvent') };