Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Pointer lock #1

Merged
merged 1 commit into from

2 participants

@ricardobeat

Hi Daniel,

I've implemented the pointer lock API. It should work in the latest Chrome and FF, but in FF mozRequestPointerLock is always undefined for some reason, so webkit only for now.

@danielribeiro

Hi.

Thanks for the pull request. It looks awesome (saw your comment on HN). However I was unable to make it work on Google Chrome (Enable Pointer is set on chrome://flags/). If you have any ideas why, let me know and I'll gladly merge it.

Cheers,

  • Daniel
@ricardobeat

@danielribeiro did you restart Chrome after changing the flag? I'm on 21.0.1180.41 beta, OSX, maybe the API is not working yet on version 20 (current stable)?

@danielribeiro

I'm on Version 20.0.1132.47, mac os X, maybe that's why.

@danielribeiro

Wow! It does work. Chrome 21 is now standard, so testing it was amazing. There is one very glaring bug: the pointer position is not the cursor position, so the clicks are off... (putting and removing the wrong block).

@ricardobeat

Yeah, I used innerHeight|Width/2 as the anchor point but it's not working as intended.

@danielribeiro danielribeiro merged commit 7fd0b29 into from
@danielribeiro

I merged this, but since so many code changed since them, i'll be cherry picking it. Thanks for the pull request, and sorry for the delayed feedback.

@ricardobeat

Sorry for neglecting the fork, I see lots of conflicts. Feel free to rewrite it all, might be less time-consuming.

Now the pointer lock API is already available in Chrome and Firefox :metal:

@danielribeiro

No problem @ricardobeat! For reasons I cannot disclose + new job, this was set aside. I'll be cherry picking from it though. I also need to add full screen mode, and I plan on add a jump of goodness.

WebGL has been exciting recently and I'll write a new blog post about the cool things that changed ever since I first pushed this.

Again, thanks so much for the pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 16, 2012
  1. @ricardobeat

    Implement Pointer Lock API

    ricardobeat authored
This page is out of date. Refresh to see the latest.
View
62 lib/camera.coffee
@@ -12,12 +12,16 @@ class Controls
@lookSpeed = 0.20
@mouseX = 0
@mouseY = 0
+ @deltaX = 0
+ @deltaY = 0
@lat = 0
@lon = 0
@mouseDragOn = false
@anchorx = null
@anchory = null
+ @mouseLocked = false
@defineBindings()
+ @enablePointerLock()
defineBindings: ->
$(@domElement).mousemove (e) => @onMouseMove e
@@ -25,6 +29,21 @@ class Controls
$(@domElement).mouseup (e) => @onMouseUp e
$(@domElement).mouseenter (e) => @onMouserEnter e
+ enablePointerLock: ->
+ @domElement.requestPointerLock ?=
+ @domElement.webkitRequestPointerLock or
+ @domElement.mozRequestPointerLock
+ $(document).bind 'pointerlockchange mozpointerlockchange webkitpointerlockchange', =>
+ d = document
+ @mouseLocked = (d.pointerLockElement or d.mozPointerLockElement or d.webkitPointerLockElement)?
+ if @mouseLocked then @showCrosshair() else @hideCrosshair()
+
+ lockPointer: ->
+ @domElement.requestPointerLock?()
+
+ showCrosshair: -> document.getElementById('cursor').style.display = 'block'
+ hideCrosshair: -> document.getElementById('cursor').style.display = 'none'
+
onMouserEnter: (event) ->
@onMouseUp(event) unless MouseEvent.isLeftButtonDown event
@@ -33,7 +52,7 @@ class Controls
@domElement.focus() if @domElement isnt document
@anchorx = event.pageX
@anchory = event.pageY
- @setMouse event
+ @setMouse @anchorx, @anchory
@mouseDragOn = true
return false
@@ -41,13 +60,23 @@ class Controls
@mouseDragOn = false
return false
- setMouse: (event) ->
- @mouseX = event.pageX
- @mouseY = event.pageY
+ setMouse: (x, y) ->
+ @mouseX = x
+ @mouseY = y
+ @setDelta x - @anchorx, y - @anchory
+
+ setDelta: (x, y) ->
+ @deltaX = x
+ @deltaY = y
onMouseMove: (event) ->
- return unless @mouseDragOn
- @setMouse event
+ if @mouseDragOn
+ @setMouse event.pageX, event.pageY
+ else if @mouseLocked
+ e = event.originalEvent
+ x = e.movementX or e.mozMovementX or e.webkitMovementX
+ y = e.movementY or e.mozMovementY or e.webkitMovementY
+ @setDelta x, y
return
halfCircle: Math.PI / 180
@@ -71,13 +100,22 @@ class Controls
return
update: ->
- return unless @mouseDragOn
- return if @mouseX is @anchorx and @mouseY is @anchory
+ return unless @mouseDragOn or @mouseLocked
+ return if @mouseDragOn and @mouseX is @anchorx and @mouseY is @anchory
{max, min} = Math
- @lon += (@mouseX - @anchorx) * @lookSpeed
- @lat -= (@mouseY - @anchory) * @lookSpeed
- @anchorx = @mouseX
- @anchory = @mouseY
+ if @mouseLocked
+ return if @deltaX is @previousDeltaX and @deltaY is @previousDeltaY
+ @previousDeltaX = @deltaX
+ @previousDeltaY = @deltaY
+ @anchorx = window.innerWidth/2
+ @anchory = window.innerHeight/2
+ else if @mouseDragOn
+ return if @mouseX is @anchorx and @mouseY is @anchory
+ @anchorx = @mouseX
+ @anchory = @mouseY
+
+ @lon += @deltaX * @lookSpeed
+ @lat -= @deltaY * @lookSpeed
@lat = max(-85, min(85, @lat))
@updateLook()
return
View
6 lib/minecraft.coffee
@@ -617,16 +617,16 @@ class Instructions
img: (name) -> "<img src='./instructions/#{name}.png'/>"
-
-
window.init_web_app = ->
+ game = null
$("#blocks").hide()
$('#instructions').hide()
$(document).bind "contextmenu", -> false
+ $(document).bind "keydown", 'c', -> game?.controls.lockPointer()
return Detector.addGetWebGLMessage() unless Detector.webgl
startGame = ->
game = new Game()
new BlockSelection(game).insert()
game.start()
+ return
new Instructions(startGame).insert()
-
View
98 public/camera.js
@@ -1,4 +1,3 @@
-// Generated by CoffeeScript 1.3.3
(function() {
var Controls, MouseEvent;
@@ -23,12 +22,16 @@
this.lookSpeed = 0.20;
this.mouseX = 0;
this.mouseY = 0;
+ this.deltaX = 0;
+ this.deltaY = 0;
this.lat = 0;
this.lon = 0;
this.mouseDragOn = false;
this.anchorx = null;
this.anchory = null;
+ this.mouseLocked = false;
this.defineBindings();
+ this.enablePointerLock();
}
Controls.prototype.defineBindings = function() {
@@ -47,22 +50,47 @@
});
};
- Controls.prototype.onMouserEnter = function(event) {
- if (!MouseEvent.isLeftButtonDown(event)) {
- return this.onMouseUp(event);
+ Controls.prototype.enablePointerLock = function() {
+ var _base,
+ _this = this;
+ if ((_base = this.domElement).requestPointerLock == null) {
+ _base.requestPointerLock = this.domElement.webkitRequestPointerLock || this.domElement.mozRequestPointerLock;
}
+ return $(document).bind('pointerlockchange mozpointerlockchange webkitpointerlockchange', function() {
+ var d;
+ d = document;
+ _this.mouseLocked = (d.pointerLockElement || d.mozPointerLockElement || d.webkitPointerLockElement) != null;
+ if (_this.mouseLocked) {
+ return _this.showCrosshair();
+ } else {
+ return _this.hideCrosshair();
+ }
+ });
+ };
+
+ Controls.prototype.lockPointer = function() {
+ var _base;
+ return typeof (_base = this.domElement).requestPointerLock === "function" ? _base.requestPointerLock() : void 0;
+ };
+
+ Controls.prototype.showCrosshair = function() {
+ return document.getElementById('cursor').style.display = 'block';
+ };
+
+ Controls.prototype.hideCrosshair = function() {
+ return document.getElementById('cursor').style.display = 'none';
+ };
+
+ Controls.prototype.onMouserEnter = function(event) {
+ if (!MouseEvent.isLeftButtonDown(event)) return this.onMouseUp(event);
};
Controls.prototype.onMouseDown = function(event) {
- if (!MouseEvent.isLeftButton(event)) {
- return;
- }
- if (this.domElement !== document) {
- this.domElement.focus();
- }
+ if (!MouseEvent.isLeftButton(event)) return;
+ if (this.domElement !== document) this.domElement.focus();
this.anchorx = event.pageX;
this.anchory = event.pageY;
- this.setMouse(event);
+ this.setMouse(this.anchorx, this.anchory);
this.mouseDragOn = true;
return false;
};
@@ -72,16 +100,27 @@
return false;
};
- Controls.prototype.setMouse = function(event) {
- this.mouseX = event.pageX;
- return this.mouseY = event.pageY;
+ Controls.prototype.setMouse = function(x, y) {
+ this.mouseX = x;
+ this.mouseY = y;
+ return this.setDelta(x - this.anchorx, y - this.anchory);
+ };
+
+ Controls.prototype.setDelta = function(x, y) {
+ this.deltaX = x;
+ return this.deltaY = y;
};
Controls.prototype.onMouseMove = function(event) {
- if (!this.mouseDragOn) {
- return;
+ var e, x, y;
+ if (this.mouseDragOn) {
+ this.setMouse(event.pageX, event.pageY);
+ } else if (this.mouseLocked) {
+ e = event.originalEvent;
+ x = e.movementX || e.mozMovementX || e.webkitMovementX;
+ y = e.movementY || e.mozMovementY || e.webkitMovementY;
+ this.setDelta(x, y);
}
- this.setMouse(event);
};
Controls.prototype.halfCircle = Math.PI / 180;
@@ -111,17 +150,26 @@
Controls.prototype.update = function() {
var max, min;
- if (!this.mouseDragOn) {
- return;
- }
- if (this.mouseX === this.anchorx && this.mouseY === this.anchory) {
+ if (!(this.mouseDragOn || this.mouseLocked)) return;
+ if (this.mouseDragOn && this.mouseX === this.anchorx && this.mouseY === this.anchory) {
return;
}
max = Math.max, min = Math.min;
- this.lon += (this.mouseX - this.anchorx) * this.lookSpeed;
- this.lat -= (this.mouseY - this.anchory) * this.lookSpeed;
- this.anchorx = this.mouseX;
- this.anchory = this.mouseY;
+ if (this.mouseLocked) {
+ if (this.deltaX === this.previousDeltaX && this.deltaY === this.previousDeltaY) {
+ return;
+ }
+ this.previousDeltaX = this.deltaX;
+ this.previousDeltaY = this.deltaY;
+ this.anchorx = window.innerWidth / 2;
+ this.anchory = window.innerHeight / 2;
+ } else if (this.mouseDragOn) {
+ if (this.mouseX === this.anchorx && this.mouseY === this.anchory) return;
+ this.anchorx = this.mouseX;
+ this.anchory = this.mouseY;
+ }
+ this.lon += this.deltaX * this.lookSpeed;
+ this.lat -= this.deltaY * this.lookSpeed;
this.lat = max(-85, min(85, this.lat));
this.updateLook();
};
View
17 public/index.html
@@ -82,6 +82,22 @@
text-align: center;
}
+ #cursor {
+ display:none;
+ position:absolute;
+ top:50%;
+ left:50%;
+ width:20px;
+ margin-left:-10px;
+ font-size:18px;
+ text-align:center;
+ color:#fff;
+ z-index:2;
+ }
+ #cursor::before {
+ content: '+'
+ }
+
#oldie a { color:#fff }
body {
@@ -94,6 +110,7 @@
<body>
<div id="instructions"></div>
<div id="container"></div>
+ <div id="cursor"></div>
<div id="blocks">
</div>
</body>
View
144 public/minecraft.js
@@ -1,7 +1,6 @@
-// Generated by CoffeeScript 1.3.3
(function() {
var AmbientLight, BlockSelection, Blocks, ClampToEdgeWrapping, Clock, CollisionHelper, CubeGeometry, CubeSize, DirectionalLight, Floor, Game, Grid, Instructions, LinearMipMapLinearFilter, Matrix4, Mesh, MeshLambertMaterial, MeshNormalMaterial, NearestFilter, Object3D, PerspectiveCamera, PlaneGeometry, Player, PointLight, Projector, Ray, RepeatWrapping, Scene, Texture, TextureHelper, UVMapping, Vector2, Vector3, WebGLRenderer, vec,
- __slice = [].slice;
+ __slice = Array.prototype.slice;
Object3D = THREE.Object3D, Matrix4 = THREE.Matrix4, Scene = THREE.Scene, Mesh = THREE.Mesh, WebGLRenderer = THREE.WebGLRenderer, PerspectiveCamera = THREE.PerspectiveCamera;
@@ -47,9 +46,7 @@
};
Player.prototype.position = function(axis) {
- if (axis == null) {
- return this.pos;
- }
+ if (axis == null) return this.pos;
return this.pos[axis];
};
@@ -139,19 +136,13 @@
CollisionHelper.prototype.collides = function() {
var cube, playerBox, _i, _len, _ref;
- if (this.player.collidesWithGround()) {
- return true;
- }
- if (this.beyondBounds()) {
- return true;
- }
+ if (this.player.collidesWithGround()) return true;
+ if (this.beyondBounds()) return true;
playerBox = this.player.boundingBox();
_ref = this.possibleCubes();
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
cube = _ref[_i];
- if (this._collideWithCube(playerBox, cube)) {
- return true;
- }
+ if (this._collideWithCube(playerBox, cube)) return true;
}
return false;
};
@@ -160,9 +151,7 @@
var p, x, y, z, _ref;
p = this.player.position();
_ref = this.grid.gridCoords(p.x, p.y, p.z), x = _ref[0], y = _ref[1], z = _ref[2];
- if (!this.grid.insideGrid(x, 0, z)) {
- return true;
- }
+ if (!this.grid.insideGrid(x, 0, z)) return true;
};
CollisionHelper.prototype._addToPosition = function(position, value) {
@@ -196,9 +185,7 @@
this.withRange(function(x, y, z) {
var cube;
cube = grid.get(x, y, z);
- if (cube != null) {
- return cubes.push(cube);
- }
+ if (cube != null) return cubes.push(cube);
});
return cubes;
};
@@ -230,12 +217,8 @@
CollisionHelper.prototype.toGrid = function(val) {
var ret;
ret = Math.floor(val / this.rad);
- if (ret < 0) {
- return 0;
- }
- if (ret > this.grid.size - 1) {
- return this.grid.size - 1;
- }
+ if (ret < 0) return 0;
+ if (ret > this.grid.size - 1) return this.grid.size - 1;
return ret;
};
@@ -407,17 +390,15 @@
};
Game.prototype.populateWorld = function() {
- var data, height, i, j, middle, middlePos, playerHeight, _i, _j,
+ var data, height, i, j, middle, middlePos, playerHeight,
_this = this;
middle = this.grid.size / 2;
data = this.generateHeight();
playerHeight = null;
- for (i = _i = -5; _i <= 5; i = ++_i) {
- for (j = _j = -5; _j <= 5; j = ++_j) {
+ for (i = -5; i <= 5; i++) {
+ for (j = -5; j <= 5; j++) {
height = (Math.abs(Math.floor(data[i + 5][j + 5]))) + 1;
- if (i === 0 && j === 0) {
- playerHeight = (height + 1) * CubeSize;
- }
+ if (i === 0 && j === 0) playerHeight = (height + 1) * CubeSize;
height.times(function(k) {
return _this.cubeAt(middle + i, k, middle + j);
});
@@ -435,11 +416,7 @@
halfcube = CubeSize / 2;
mesh.position.set(CubeSize * x, y * CubeSize + halfcube, CubeSize * z);
mesh.name = "block";
- if (validatingFunction != null) {
- if (!validatingFunction(mesh)) {
- return;
- }
- }
+ if (validatingFunction != null) if (!validatingFunction(mesh)) return;
this.grid.put(x, y, z, mesh);
this.scene.add(mesh);
mesh.updateMatrix();
@@ -507,9 +484,7 @@
Game.prototype.togglePause = function() {
this.pause = !this.pause;
- if (this.pause === false) {
- this.clock.start();
- }
+ if (this.pause === false) this.clock.start();
};
Game.prototype.onMouseUp = function(event) {
@@ -525,17 +500,13 @@
Game.prototype.onMouseDown = function(event) {
this.moved = false;
- if (!MouseEvent.isRightButton(event)) {
- return;
- }
+ if (!MouseEvent.isRightButton(event)) return;
return this.castRay = [event.pageX, event.pageY];
};
Game.prototype.deleteBlock = function() {
var todir, vector, x, y, _ref;
- if (this.toDelete == null) {
- return;
- }
+ if (this.toDelete == null) return;
_ref = this.toDelete, x = _ref[0], y = _ref[1];
x = (x / this.width) * 2 - 1;
y = (-y / this.height) * 2 + 1;
@@ -551,9 +522,7 @@
_ref = ray.intersectScene(this.scene);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
o = _ref[_i];
- if (o.object.name !== 'floor') {
- return o;
- }
+ if (o.object.name !== 'floor') return o;
}
return null;
};
@@ -561,12 +530,8 @@
Game.prototype.deleteBlockInGrid = function(ray) {
var mesh, target, x, y, z, _ref;
target = this.findBlock(ray);
- if (target == null) {
- return;
- }
- if (!this.withinHandDistance(target.object.position)) {
- return;
- }
+ if (target == null) return;
+ if (!this.withinHandDistance(target.object.position)) return;
mesh = target.object;
this.scene.remove(mesh);
_ref = mesh.position, x = _ref.x, y = _ref.y, z = _ref.z;
@@ -575,9 +540,7 @@
Game.prototype.placeBlock = function() {
var todir, vector, x, y, _ref;
- if (this.castRay == null) {
- return;
- }
+ if (this.castRay == null) return;
_ref = this.castRay, x = _ref[0], y = _ref[1];
x = (x / this.width) * 2 - 1;
y = (-y / this.height) * 2 + 1;
@@ -604,9 +567,7 @@
Game.prototype.getCubeOnFloorPosition = function(ray) {
var o, ret, t, v;
- if (ray.direction.y >= 0) {
- return null;
- }
+ if (ray.direction.y >= 0) return null;
ret = vec();
o = ray.origin;
v = ray.direction;
@@ -624,9 +585,7 @@
Game.prototype.getNewCubePosition = function(ray) {
var target;
target = this.findBlock(ray);
- if (target == null) {
- return this.getCubeOnFloorPosition(ray);
- }
+ if (target == null) return this.getCubeOnFloorPosition(ray);
return this.getAdjacentCubePosition(target);
};
@@ -648,20 +607,12 @@
Game.prototype.placeBlockInGrid = function(ray) {
var gridPos, p, x, y, z;
p = this.getNewCubePosition(ray);
- if (p == null) {
- return;
- }
+ if (p == null) return;
gridPos = this.gridCoords(p.x, p.y, p.z);
x = gridPos[0], y = gridPos[1], z = gridPos[2];
- if (!this.withinHandDistance(p)) {
- return;
- }
- if (!this.grid.insideGrid(x, y, z)) {
- return;
- }
- if (this.grid.get(x, y, z) != null) {
- return;
- }
+ if (!this.withinHandDistance(p)) return;
+ if (!this.grid.insideGrid(x, y, z)) return;
+ if (this.grid.get(x, y, z) != null) return;
this.createCubeAt(x, y, z);
};
@@ -673,9 +624,7 @@
var animate,
_this = this;
animate = function() {
- if (!_this.pause) {
- _this.tick();
- }
+ if (!_this.pause) _this.tick();
return requestAnimationFrame(animate, _this.renderer.domElement);
};
return animate();
@@ -694,16 +643,12 @@
_ref = this.axes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
axis = _ref[_i];
- if (!(this.move[axis] !== 0)) {
- continue;
- }
+ if (!(this.move[axis] !== 0)) continue;
originalpos = this.player.position(axis);
this.player.incPosition(axis, this.move[axis]);
if (this.collides()) {
this.player.setPosition(axis, originalpos);
- if (axis === 'y' && this.move.y < 0) {
- this.onGround = true;
- }
+ if (axis === 'y' && this.move.y < 0) this.onGround = true;
} else if (axis === 'y' && this.move.y <= 0) {
this.onGround = false;
}
@@ -733,9 +678,7 @@
action = _ref[key];
axis = action[0], operation = action[1];
vel = operation === '-' ? -baseVel : baseVel;
- if (this.keysDown[key]) {
- this.move[axis] += vel;
- }
+ if (this.keysDown[key]) this.move[axis] += vel;
}
if (this.shouldJump()) {
this.onGround = false;
@@ -766,9 +709,7 @@
};
Game.prototype.applyGravity = function() {
- if (!(this.move.y < -1)) {
- return this.move.y -= .005;
- }
+ if (!(this.move.y < -1)) return this.move.y -= .005;
};
Game.prototype.setCameraEyes = function() {
@@ -810,9 +751,7 @@
};
BlockSelection.prototype.mousedown = function(e) {
- if (e.target === this) {
- return false;
- }
+ if (e.target === this) return false;
this.select(e.target.id);
return false;
};
@@ -833,9 +772,7 @@
};
BlockSelection.prototype.select = function(name) {
- if (this.current === name) {
- return;
- }
+ if (this.current === name) return;
this.game.selectCubeBlock(name);
this.ligthUp(name);
this.lightOff(this.current);
@@ -958,20 +895,21 @@
})();
window.init_web_app = function() {
- var startGame;
+ var game, startGame;
+ game = null;
$("#blocks").hide();
$('#instructions').hide();
$(document).bind("contextmenu", function() {
return false;
});
- if (!Detector.webgl) {
- return Detector.addGetWebGLMessage();
- }
+ $(document).bind("keydown", 'c', function() {
+ return game != null ? game.controls.lockPointer() : void 0;
+ });
+ if (!Detector.webgl) return Detector.addGetWebGLMessage();
startGame = function() {
- var game;
game = new Game();
new BlockSelection(game).insert();
- return game.start();
+ game.start();
};
return new Instructions(startGame).insert();
};
Something went wrong with that request. Please try again.