Question

I'm trying to use a gamepad to rotate a camera in Three.js, using first-person-shooter style controls.

The browser detects the gamepad and recognises it's input, but the camera's order of rotation is wrong. When I rotate on the camera's local Y axis, it takes into account the local X rotation too, which is unwanted.

It seems I'm having the same problem as this guy, but his issue was solved using Three.js r54 and I'm using r60. He set camera.eulerOrder = "YXZ"; to get it working, but the current equivalent camera.rotation.order = "YXZ"; doesn't seem to work for me.

I'm aware of Three.js' built-in "FirstPersonControls" class, but it's not suitable for me as it doesn't accept controller input and it would be messy to shoehorn other non-movement controls in there later. I'm also aware of gamepad.js and have no interest in using it.

Can anyone help?

My rotation code:

function pollGamepad()
{
    gamepad = navigator.webkitGetGamepads()[0];

    //Rotation
    if(gamepad.axes[3] > 0.20)
    {
        camera.rotateX(-gamepad.axes[3] * 0.02);
    }
    if(gamepad.axes[3] < -0.20)
    {
        camera.rotateX(-gamepad.axes[3] * 0.02);
    }

    if(gamepad.axes[2] < -0.20)
    {
        camera.rotateY(-gamepad.axes[2] * 0.02);
    }
    if(gamepad.axes[2] > 0.20)
    {
        camera.rotateY(-gamepad.axes[2] * 0.02);
    }
}
Was it helpful?

Solution

The approach that PointerLockControls uses is to create a hierarchy of objects: yawObject contains pitchObject contains camera. Then horizontal mouse (or joystick) movement would change the Y-rotation of the yaw object, vertical mouse (or joystick) movement would change the X-rotation of the pitch object, and the camera's rotation would stay fixed at the default (0, 0, -1). This just manually simulates Euler YXZ ordering but it may work better for you. It does create some awkwardness if you need to get the overall rotation.

In a custom controller I wrote recently, I achieved the same result by add()ing the camera to a single parent object and setting the parent object's Euler order to YXZ. I don't remember why this worked better than setting it on the camera directly, but it did.

OTHER TIPS

I faced this problem myself and I came up with a simple solution. Always reset camera's x rotation to zero before performing a y rotation. Then, restore x rotation. So:

// Rotate camera Fps style
function rotateCamera(dx,dy){
    //store previous x rotation
    var x = camera.rotation.x;

    //reset camera's x rotation.
    camera.rotateX(-x);

    //rotate camera on y axis
    camera.rotateY(dy);

    //check if we are trying to look to high or too low
    if ( Math.abs( dx + x ) > Math.Pi/2 - 0.05) {
        camera.rotateX(x);
    else
        camera.rotateX(x+dx);

    //reset z rotation. Floating point operations might change z rotation during the above operations.
    camera.rotation.z = 0;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top