Reputation: 444
I have been playing around with webGL and I have reached a point I can make small three dimensional games with very pitiful graphics (it is more of a proof of concept/functionality as of now). For the three dimensional experience, it is nice to move the mouse in any direction infinitely and seamlessly to rotate the first person camera. Pointerlock allows me to lock and hide the cursor position, which is very helpful, but then I need to find another method of tracking the mouse's movements. In my research, event.movementX
and event.movementY
seemed to be the standard, but I often get large blips (usually between 500 and 583) of movement in the opposite direction of the mouse's movement. I tested this with numerous mice and trackpads and experience the same phenomenon.
Here are my relavent event listeners:
document.addEventListener("mousemove", function(event) {
xMovement += event.movementX;
yMovement += event.movementY;
console.log(event.movementX)
}, false);
document.addEventListener("pointerlockchange", function(event) {
if(pointerLockEnabled) pointerLockEnabled = false;
else pointerLockEnabled = true;
xMovement = 0; yMovement = 0;
} , false);
And relevant render loop code:
function render() {
if(pointerLockEnabled) {
camera.rotation.y = -xMovement / 1000;
camera.rotation.x = -yMovement / 1000;
if(rightKey && !leftKey) {
camera.position.x += 10 * Math.cos(camera.rotation.y);
camera.position.z -= 10 * Math.sin(camera.rotation.y);
}
else if(leftKey && !rightKey) {
camera.position.x -= 10 * Math.cos(camera.rotation.y);
camera.position.z += 10 * Math.sin(camera.rotation.y);
}
if(upKey&& !downKey) {
camera.position.z -= 10 * Math.cos(camera.rotation.y);
camera.position.x -= 10 * Math.sin(camera.rotation.y);
}
else if(downKey && !upKey) {
camera.position.z += 10 * Math.cos(camera.rotation.y);
camera.position.x += 10 * Math.sin(camera.rotation.y);
}
}
}
But my console has occurrences such as this:
I added conditions to changing xMovement
to prevent massive turns in camera angle, but I am still left with very annoying movement. Any ideas to patch or replace to a more seamless interface movement?
Upvotes: 2
Views: 673
Reputation: 451
I know I'm super late to the party here, but I have an explanation for the first problem mentioned in the OP, and a work-around for the second problem brought up by @KiranKota.
The first problem is actually a bug in Chromium versions prior to 64. It was dormant until something happened in the Windows 10 Fall Creator's update that ended up exposing it. Even though you would be in pointer-lock, your "cursor", though invisible, would essentially wrap around to the other side of the window, causing spikes in the opposite direction of movement.
The fix for this is to simply ignore the first mouse move event that moves in the opposite direction; that's if you still care about supporting Chromium < 67.
The second problem, where the spikes move in the same direction, is entirely unrelated, and still a problem as of Chromium 94. The issue has to do with mice with high polling rates, as is the case with many gaming mice. Through my experiments, I've discovered that a polling rate of 1000 is quite bad, 500 less so, and 250 appears to make the issue disappear. I've also discovered that the spikes are consistent with the width of the current window. They are always window.innerWidth (or innerHeight) / ~2.3... , plus what I can only assume is the "real" distance of the current mouse movement. Why 2.3-ish...? I have no idea. The factor is the same whether I'm running a rate of 1000 or 500.
I'm going to experiment with this some more and see if I can't reliably filter out these anomalies. In the mean time, perhaps this info will be useful.
UPDATE: I've settled on solving the second issue by simply ignoring any mouse movements that are greater than window.innerWidth / 3 and window.innerHeight / 3. I've provided a toggle for users to turn this solution on or off, but it doesn't seem to interfere with normal use, regardless of polling rate.
UPDATE #2: I just revisited this and discovered through the Chromium bug list that they added a new, experimental option in Chrome 88 that actually fixes this completely, so far as I can tell:
requestPointerLock({ unadjustedMovement: true })
According to MDN: "Disables OS-level adjustment for mouse acceleration, and accesses raw mouse input instead. The default value is false; setting it to true will disable mouse acceleration."
Upvotes: 5
Reputation: 103
I had the same issue but, for me, the bad values are always in the same direction as movement. I found that if I replace any values above 50 with the last value, I get very good accuracy. The only issues are when the bad values are in the 30-49 range but I don't want to cancel those in case the user is actually moving their mouse that fast or their mouse has a bad polling rate. Some trendline comparison would work for smoothing those, but if you don't need too much precision, this is good:
const movement = {X: 0, Y: 0};
const lastMovement = {X: 0, Y: 0};
function onMouseMove(evt) {
if (checkPointerLock()) {
['X', 'Y'].forEach(axis => {
const checkValue = evt['movement' + axis] || evt['mozMovement' + axis]|| 0;
//ignore >=50. probably erroneous. smooth to last value
if (Math.abs(checkValue) < 50) {
lastMovement[axis] = checkValue;
}
movement[axis] = lastMovement[axis];
});
yaw += movement.X * -0.001 * data.sensitivityX;
pitch += movement.Y * (data.invertY ? .001 : -.001) * data.sensitivityY;
pitch = Math.max(-Math.PI/2, Math.min(Math.PI/2, pitch));
}
}
Upvotes: 0
Reputation: 20734
It could be helpful if you would throttle your mousemove event in some way. For example the lodash throttle version:
function handleMouseMove(event) {
xMovement += event.movementX;
yMovement += event.movementY;
console.log(event.movementX)
}
var throttledHandleMouseMove = _.throttle(handleMouseMove, 75);
document.addEventListener("mousemove", throttledHandleMouseMove, false);
With this approach handleMouseMove
would not be executed more that 1 time per 75ms.
Upvotes: 1