Skip to content

Commit d563cab

Browse files
committed
Improved pointer lock api
Some improvements over v2: - You can access movementXY on pointer without needing to perform a manual `resetMovement()` - Fixes bug where `releasePointerLock` would unregister event listeners before a final `pointerLockChange` could be called. Results in mouse.locked not having the right state and a final 'POINTER_LOCK_CHANGE_EVENT' not firing.
1 parent b5e8a60 commit d563cab

5 files changed

Lines changed: 117 additions & 1 deletion

File tree

v3/src/input/Pointer.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,18 @@ var Pointer = new Class({
6565
this.justDown = false;
6666
this.justUp = false;
6767
this.justMoved = false;
68+
69+
/**
70+
* @property {number} movementX - If the mouse is locked, the horizontal relative movement
71+
* of the Pointer in pixels since last frame.
72+
*/
73+
this.movementX = 0;
74+
75+
/**
76+
* @property {number} movementY - If the mouse is locked, the vertical relative movement of
77+
* the Pointer in pixels since last frame.
78+
*/
79+
this.movementY = 0;
6880
},
6981

7082
positionToCamera: function (camera, output)
@@ -109,6 +121,8 @@ var Pointer = new Class({
109121
this.justDown = false;
110122
this.justUp = false;
111123
this.justMoved = false;
124+
this.movementX = 0;
125+
this.movementY = 0;
112126
},
113127

114128
touchmove: function (event, time)
@@ -135,6 +149,13 @@ var Pointer = new Class({
135149
this.x = this.manager.transformX(event.pageX);
136150
this.y = this.manager.transformY(event.pageY);
137151

152+
if (this.manager.mouse.locked)
153+
{
154+
// Potentially multiple DOM events within one frame, but only one Phaser event will fire
155+
this.movementX += event.movementX || event.mozMovementX || event.webkitMovementX || 0;
156+
this.movementY += event.movementY || event.mozMovementY || event.webkitMovementY || 0;
157+
}
158+
138159
this.justMoved = true;
139160

140161
this.dirty = true;

v3/src/input/global/GlobalInputManager.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ var GlobalInputManager = new Class({
113113
this.events.dispatch(new MouseEvent.UP(event));
114114
break;
115115

116+
case 'pointerlockchange':
117+
118+
this.events.dispatch(new MouseEvent.POINTER_LOCK_CHANGE(event, this.mouse.locked));
119+
break;
120+
116121
case 'touchmove':
117122

118123
pointer.touchmove(event, time);

v3/src/input/mouse/MouseManager.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
var Class = require('../../utils/Class');
2+
var Features = require('../../device/Features');
3+
var MouseEvents = require('./events');
24

35
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
46
// https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
@@ -19,6 +21,12 @@ var MouseManager = new Class({
1921
this.target;
2022

2123
this.handler;
24+
25+
/**
26+
* @property {boolean} locked - If the mouse has been pointer locked successfully this will
27+
* be set to true.
28+
*/
29+
this.locked = false;
2230
},
2331

2432
boot: function ()
@@ -55,6 +63,52 @@ var MouseManager = new Class({
5563
return this;
5664
},
5765

66+
/**
67+
* If the browser supports it, you can request that the pointer be locked to the browser window.
68+
* This is classically known as 'FPS controls', where the pointer can't leave the browser until
69+
* the user presses an exit key. If the browser successfully enters a locked state, a
70+
* 'POINTER_LOCK_CHANGE_EVENT' will be dispatched - from the game's input manager - with an
71+
* `isPointerLocked` property.
72+
* It is important to note that pointer lock can only be enabled after an 'engagement gesture',
73+
* see: https://w3c.github.io/pointerlock/#dfn-engagement-gesture.
74+
*/
75+
requestPointerLock: function ()
76+
{
77+
if (Features.pointerLock)
78+
{
79+
var element = this.target;
80+
element.requestPointerLock = element.requestPointerLock || element.mozRequestPointerLock || element.webkitRequestPointerLock;
81+
element.requestPointerLock();
82+
}
83+
},
84+
85+
/**
86+
* Internal pointerLockChange handler.
87+
*
88+
* @param {Event} event - The native event from the browser.
89+
*/
90+
pointerLockChange: function (event)
91+
{
92+
var element = this.target;
93+
this.locked = document.pointerLockElement === element || document.mozPointerLockElement === element || document.webkitPointerLockElement === element
94+
? true : false;
95+
this.manager.queue.push(event);
96+
},
97+
98+
/**
99+
* If the browser supports pointer lock, this will request that the pointer lock is released. If
100+
* the browser successfully enters a locked state, a 'POINTER_LOCK_CHANGE_EVENT' will be
101+
* dispatched - from the game's input manager - with an `isPointerLocked` property.
102+
*/
103+
releasePointerLock: function ()
104+
{
105+
if (Features.pointerLock)
106+
{
107+
document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock || document.webkitExitPointerLock;
108+
document.exitPointerLock();
109+
}
110+
},
111+
58112
startListeners: function ()
59113
{
60114
var queue = this.manager.queue;
@@ -82,13 +136,28 @@ var MouseManager = new Class({
82136
this.target.addEventListener('mousemove', handler, false);
83137
this.target.addEventListener('mousedown', handler, false);
84138
this.target.addEventListener('mouseup', handler, false);
139+
140+
if (Features.pointerLock)
141+
{
142+
this.pointerLockChange = this.pointerLockChange.bind(this);
143+
document.addEventListener('pointerlockchange', this.pointerLockChange, true);
144+
document.addEventListener('mozpointerlockchange', this.pointerLockChange, true);
145+
document.addEventListener('webkitpointerlockchange', this.pointerLockChange, true);
146+
}
85147
},
86148

87149
stopListeners: function ()
88150
{
89151
this.target.removeEventListener('mousemove', this.handler);
90152
this.target.removeEventListener('mousedown', this.handler);
91153
this.target.removeEventListener('mouseup', this.handler);
154+
155+
if (Features.pointerLock)
156+
{
157+
document.removeEventListener('pointerlockchange', this.pointerLockChange, true);
158+
document.removeEventListener('mozpointerlockchange', this.pointerLockChange, true);
159+
document.removeEventListener('webkitpointerlockchange', this.pointerLockChange, true);
160+
}
92161
}
93162

94163
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
var Class = require('../../../utils/Class');
2+
var Event = require('../../../events/Event');
3+
4+
var PointerLockChangeEvent = new Class({
5+
6+
Extends: Event,
7+
8+
initialize:
9+
10+
function PointerLockChangeEvent (nativeEvent, isPointerLocked)
11+
{
12+
Event.call(this, 'POINTER_LOCK_CHANGE_EVENT');
13+
14+
this.data = nativeEvent;
15+
this.isPointerLocked = isPointerLocked;
16+
}
17+
18+
});
19+
20+
module.exports = PointerLockChangeEvent;

v3/src/input/mouse/events/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
module.exports = {
44
DOWN: require('./MouseDownEvent'),
55
UP: require('./MouseUpEvent'),
6-
MOVE: require('./MouseMoveEvent')
6+
MOVE: require('./MouseMoveEvent'),
7+
POINTER_LOCK_CHANGE: require('./PointerLockChangeEvent')
78
};

0 commit comments

Comments
 (0)