admin管理员组

文章数量:1287491

I am making a 3d Three Js game, but I have run into a problem with my camera controls. When I look the starting direction that you look, the camera controls are all right. But if I look backward, the up and down camera controls inverse, and if I look to one of the sides, then those controls make the whole thing go sideways. I think the problem is like an old time joystick, where if you stand on the side of the claw machine or whatever it is, than forward would not be forward, it would be to the side. Here is my code so far: (Sorry, mouse lock doesn't work with stack overflow, use something like .asp?filename=tryhtml_default)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>First Person Game</title>
    <style>
        body { margin: 0; }
        canvas { display: block; }
    </style>
</head>
<body>
<script src=".js/r128/three.min.js"></script>
<script>
    let scene, camera, renderer;
    let moveForward = false, moveBackward = false, moveLeft = false, moveRight = false;
    let velocity = new THREE.Vector3();
    let direction = new THREE.Vector3();
    let canJump = false;
    let prevTime = performance.now();
    let cubes = [], cubeVelocities = [];
    let heldCube = null;

    const speed = 150.0;
    const jumpVelocity = 75.0;
    const gravity = 9.8 * 50.0;
    const pickUpDistance = 2.0;
    const originalCubeScale = 1;
    const heldCubeScale = 0.5;
    const friction = 0.1; // Friction coefficient
    let pitch = 0, yaw = 0;

    init();
    animate();

    function init() {
        // Scene and camera setup
        scene = new THREE.Scene();
        camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.y = 1.6;

        // Renderer setup
        renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // Ground
        const groundGeometry = new THREE.PlaneGeometry(50, 50);
        const groundMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
        const ground = new THREE.Mesh(groundGeometry, groundMaterial);
        ground.rotation.x = -Math.PI / 2;
        scene.add(ground);

        // Tangible cubes
        const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
        const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0x0000ff });
        for (let i = 0; i < 3; i++) {
            const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
            cube.position.set(Math.random() * 30 - 15, 1.6, Math.random() * 30 - 15);
            cubes.push(cube);
            cubeVelocities.push(new THREE.Vector3());
            scene.add(cube);
        }

        // Event listeners for movement
        document.addEventListener('keydown', onKeyDown);
        document.addEventListener('keyup', onKeyUp);

        // Lock the mouse
        document.body.addEventListener('click', () => {
            document.body.requestPointerLock();
        });
        document.addEventListener('mousemove', onMouseMove);

        window.addEventListener('resize', onWindowResize);
    }

    function onWindowResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
    }

    function onKeyDown(event) {
        switch (event.code) {
            case 'KeyW': moveForward = true; break;
            case 'KeyS': moveBackward = true; break;
            case 'KeyA': moveLeft = true; break; 
            case 'KeyD': moveRight = true; break;
            case 'Space':
                if (canJump) {
                    velocity.y = jumpVelocity;
                    canJump = false;
                }
                break;
            case 'KeyF':
                pickOrThrowCube();
                break;
        }
    }

    function onKeyUp(event) {
        switch (event.code) {
            case 'KeyW': moveForward = false; break;
            case 'KeyS': moveBackward = false; break;
            case 'KeyA': moveLeft = false; break; 
            case 'KeyD': moveRight = false; break;
        }
    }

    function onMouseMove(event) {
        if (document.pointerLockElement) {
            const sensitivity = 0.002;

            yaw -= event.movementX * sensitivity;
            pitch -= event.movementY * sensitivity;
            pitch = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, pitch));

            camera.rotation.y = yaw;
            camera.rotation.x = pitch;
            camera.rotation.z = 0; // Prevent rolling sideways
        }
    }

    function pickOrThrowCube() {
        if (heldCube) {
            // Throw the held cube
            const throwVelocity = new THREE.Vector3();
            camera.getWorldDirection(throwVelocity);
            throwVelocity.multiplyScalar(200); // Throw strength
            heldCube.position.add(throwVelocity.multiplyScalar(0.02));
            cubeVelocities[cubes.indexOf(heldCube)].copy(throwVelocity);
            heldCube.scale.set(originalCubeScale, originalCubeScale, originalCubeScale); // Reset cube scale
            heldCube = null;
        } else {
            // Pick up a cube if close enough and looking at it
            const raycaster = new THREE.Raycaster();
            raycaster.setFromCamera(new THREE.Vector2(0, 0), camera);
            const intersects = raycaster.intersectObjects(cubes);

            if (intersects.length > 0) {
                const intersect = intersects[0];
                if (intersect.distance < pickUpDistance) {
                    heldCube = intersect.object;
                    cubeVelocities[cubes.indexOf(heldCube)].set(0, 0, 0); // Stop the cube's movement
                    heldCube.scale.set(heldCubeScale, heldCubeScale, heldCubeScale); // Make the cube smaller
                    heldCube.position.copy(camera.position).add(camera.getWorldDirection(new THREE.Vector3()).multiplyScalar(1.5));
                }
            }
        }
    }

    function animate() {
        requestAnimationFrame(animate);

        // Update time
        const time = performance.now();
        const delta = (time - prevTime) / 1000;

        // Movement logic
        direction.z = Number(moveForward) - Number(moveBackward);
        direction.x = Number(moveRight) - Number(moveLeft);
        direction.normalize();

        velocity.x -= velocity.x * 10.0 * delta;
        velocity.z -= velocity.z * 10.0 * delta;
        velocity.y -= gravity * delta;

        if (moveForward || moveBackward || moveLeft || moveRight) {
            const frontDirection = new THREE.Vector3();
            camera.getWorldDirection(frontDirection);

            const rightDirection = new THREE.Vector3();
            rightDirection.crossVectors(camera.up, frontDirection).normalize();

            frontDirection.multiplyScalar(direction.z * speed * delta);
            rightDirection.multiplyScalar(direction.x * speed * delta);

            velocity.add(frontDirection).add(rightDirection);
        }

        camera.position.addScaledVector(velocity, delta);

        // Collision detection with barriers
        camera.position.x = Math.max(-24, Math.min(24, camera.position.x));
        camera.position.z = Math.max(-24, Math.min(24, camera.position.z));

        if (camera.position.y < 1.6) {
            velocity.y = 0;
            camera.position.y = 1.6;
            canJump = true;
        }

        // Update held cube position
        if (heldCube) {
            heldCube.position.copy(camera.position).add(camera.getWorldDirection(new THREE.Vector3()).multiplyScalar(1.5));
        }

        // Cube collision logic
        for (let i = 0; i < cubes.length; i++) {
            if (cubes[i] !== heldCube) {
                // Apply friction
                cubeVelocities[i].x *= (1 - friction);
                cubeVelocities[i].z *= (1 - friction);

                // Apply gravity to cubes
                cubeVelocities[i].y -= gravity * delta;
                cubes[i].position.addScaledVector(cubeVelocities[i], delta);

                // Cube collision with ground: stop bouncing
                if (cubes[i].position.y < 0.5) {
                    cubes[i].position.y = 0.5;
                    cubeVelocities[i].y = 0; // Stop upward velocity
                }

                // Simple cube interaction logic for pushing
                for (let j = 0; j < cubes.length; j++) {
                    if (i !== j) {
                        const distance = cubes[i].position.distanceTo(cubes[j].position);
                        if (distance < 1.5) { // If cubes are close enough
                            const collisionDirection = new THREE.Vector3().subVectors(cubes[i].position, cubes[j].position).normalize();
                            const relativeVelocity = new THREE.Vector3().subVectors(cubeVelocities[i], cubeVelocities[j]);
                            if (relativeVelocity.dot(collisionDirection) < 0) { // Only push if moving toward each other
                                const pushAmount = 0.02; // Adjust to control push force
                                cubeVelocities[i].add(collisionDirection.clone().multiplyScalar(pushAmount));
                                cubeVelocities[j].sub(collisionDirection.clone().multiplyScalar(pushAmount));
                            }
                        }
                    }
                }
            }
        }

        renderer.render(scene, camera);
        prevTime = time;
    }
</script>
</body>
</html>

I am making a 3d Three Js game, but I have run into a problem with my camera controls. When I look the starting direction that you look, the camera controls are all right. But if I look backward, the up and down camera controls inverse, and if I look to one of the sides, then those controls make the whole thing go sideways. I think the problem is like an old time joystick, where if you stand on the side of the claw machine or whatever it is, than forward would not be forward, it would be to the side. Here is my code so far: (Sorry, mouse lock doesn't work with stack overflow, use something like https://www.w3schools/html/tryit.asp?filename=tryhtml_default)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>First Person Game</title>
    <style>
        body { margin: 0; }
        canvas { display: block; }
    </style>
</head>
<body>
<script src="https://cdnjs.cloudflare/ajax/libs/three.js/r128/three.min.js"></script>
<script>
    let scene, camera, renderer;
    let moveForward = false, moveBackward = false, moveLeft = false, moveRight = false;
    let velocity = new THREE.Vector3();
    let direction = new THREE.Vector3();
    let canJump = false;
    let prevTime = performance.now();
    let cubes = [], cubeVelocities = [];
    let heldCube = null;

    const speed = 150.0;
    const jumpVelocity = 75.0;
    const gravity = 9.8 * 50.0;
    const pickUpDistance = 2.0;
    const originalCubeScale = 1;
    const heldCubeScale = 0.5;
    const friction = 0.1; // Friction coefficient
    let pitch = 0, yaw = 0;

    init();
    animate();

    function init() {
        // Scene and camera setup
        scene = new THREE.Scene();
        camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.y = 1.6;

        // Renderer setup
        renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // Ground
        const groundGeometry = new THREE.PlaneGeometry(50, 50);
        const groundMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
        const ground = new THREE.Mesh(groundGeometry, groundMaterial);
        ground.rotation.x = -Math.PI / 2;
        scene.add(ground);

        // Tangible cubes
        const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
        const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0x0000ff });
        for (let i = 0; i < 3; i++) {
            const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
            cube.position.set(Math.random() * 30 - 15, 1.6, Math.random() * 30 - 15);
            cubes.push(cube);
            cubeVelocities.push(new THREE.Vector3());
            scene.add(cube);
        }

        // Event listeners for movement
        document.addEventListener('keydown', onKeyDown);
        document.addEventListener('keyup', onKeyUp);

        // Lock the mouse
        document.body.addEventListener('click', () => {
            document.body.requestPointerLock();
        });
        document.addEventListener('mousemove', onMouseMove);

        window.addEventListener('resize', onWindowResize);
    }

    function onWindowResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
    }

    function onKeyDown(event) {
        switch (event.code) {
            case 'KeyW': moveForward = true; break;
            case 'KeyS': moveBackward = true; break;
            case 'KeyA': moveLeft = true; break; 
            case 'KeyD': moveRight = true; break;
            case 'Space':
                if (canJump) {
                    velocity.y = jumpVelocity;
                    canJump = false;
                }
                break;
            case 'KeyF':
                pickOrThrowCube();
                break;
        }
    }

    function onKeyUp(event) {
        switch (event.code) {
            case 'KeyW': moveForward = false; break;
            case 'KeyS': moveBackward = false; break;
            case 'KeyA': moveLeft = false; break; 
            case 'KeyD': moveRight = false; break;
        }
    }

    function onMouseMove(event) {
        if (document.pointerLockElement) {
            const sensitivity = 0.002;

            yaw -= event.movementX * sensitivity;
            pitch -= event.movementY * sensitivity;
            pitch = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, pitch));

            camera.rotation.y = yaw;
            camera.rotation.x = pitch;
            camera.rotation.z = 0; // Prevent rolling sideways
        }
    }

    function pickOrThrowCube() {
        if (heldCube) {
            // Throw the held cube
            const throwVelocity = new THREE.Vector3();
            camera.getWorldDirection(throwVelocity);
            throwVelocity.multiplyScalar(200); // Throw strength
            heldCube.position.add(throwVelocity.multiplyScalar(0.02));
            cubeVelocities[cubes.indexOf(heldCube)].copy(throwVelocity);
            heldCube.scale.set(originalCubeScale, originalCubeScale, originalCubeScale); // Reset cube scale
            heldCube = null;
        } else {
            // Pick up a cube if close enough and looking at it
            const raycaster = new THREE.Raycaster();
            raycaster.setFromCamera(new THREE.Vector2(0, 0), camera);
            const intersects = raycaster.intersectObjects(cubes);

            if (intersects.length > 0) {
                const intersect = intersects[0];
                if (intersect.distance < pickUpDistance) {
                    heldCube = intersect.object;
                    cubeVelocities[cubes.indexOf(heldCube)].set(0, 0, 0); // Stop the cube's movement
                    heldCube.scale.set(heldCubeScale, heldCubeScale, heldCubeScale); // Make the cube smaller
                    heldCube.position.copy(camera.position).add(camera.getWorldDirection(new THREE.Vector3()).multiplyScalar(1.5));
                }
            }
        }
    }

    function animate() {
        requestAnimationFrame(animate);

        // Update time
        const time = performance.now();
        const delta = (time - prevTime) / 1000;

        // Movement logic
        direction.z = Number(moveForward) - Number(moveBackward);
        direction.x = Number(moveRight) - Number(moveLeft);
        direction.normalize();

        velocity.x -= velocity.x * 10.0 * delta;
        velocity.z -= velocity.z * 10.0 * delta;
        velocity.y -= gravity * delta;

        if (moveForward || moveBackward || moveLeft || moveRight) {
            const frontDirection = new THREE.Vector3();
            camera.getWorldDirection(frontDirection);

            const rightDirection = new THREE.Vector3();
            rightDirection.crossVectors(camera.up, frontDirection).normalize();

            frontDirection.multiplyScalar(direction.z * speed * delta);
            rightDirection.multiplyScalar(direction.x * speed * delta);

            velocity.add(frontDirection).add(rightDirection);
        }

        camera.position.addScaledVector(velocity, delta);

        // Collision detection with barriers
        camera.position.x = Math.max(-24, Math.min(24, camera.position.x));
        camera.position.z = Math.max(-24, Math.min(24, camera.position.z));

        if (camera.position.y < 1.6) {
            velocity.y = 0;
            camera.position.y = 1.6;
            canJump = true;
        }

        // Update held cube position
        if (heldCube) {
            heldCube.position.copy(camera.position).add(camera.getWorldDirection(new THREE.Vector3()).multiplyScalar(1.5));
        }

        // Cube collision logic
        for (let i = 0; i < cubes.length; i++) {
            if (cubes[i] !== heldCube) {
                // Apply friction
                cubeVelocities[i].x *= (1 - friction);
                cubeVelocities[i].z *= (1 - friction);

                // Apply gravity to cubes
                cubeVelocities[i].y -= gravity * delta;
                cubes[i].position.addScaledVector(cubeVelocities[i], delta);

                // Cube collision with ground: stop bouncing
                if (cubes[i].position.y < 0.5) {
                    cubes[i].position.y = 0.5;
                    cubeVelocities[i].y = 0; // Stop upward velocity
                }

                // Simple cube interaction logic for pushing
                for (let j = 0; j < cubes.length; j++) {
                    if (i !== j) {
                        const distance = cubes[i].position.distanceTo(cubes[j].position);
                        if (distance < 1.5) { // If cubes are close enough
                            const collisionDirection = new THREE.Vector3().subVectors(cubes[i].position, cubes[j].position).normalize();
                            const relativeVelocity = new THREE.Vector3().subVectors(cubeVelocities[i], cubeVelocities[j]);
                            if (relativeVelocity.dot(collisionDirection) < 0) { // Only push if moving toward each other
                                const pushAmount = 0.02; // Adjust to control push force
                                cubeVelocities[i].add(collisionDirection.clone().multiplyScalar(pushAmount));
                                cubeVelocities[j].sub(collisionDirection.clone().multiplyScalar(pushAmount));
                            }
                        }
                    }
                }
            }
        }

        renderer.render(scene, camera);
        prevTime = time;
    }
</script>
</body>
</html>

Share Improve this question edited Feb 24 at 20:30 Łukasz Daniel Mastalerz 2,2172 gold badges7 silver badges26 bronze badges asked Feb 24 at 17:39 Mason FisherMason Fisher 591 silver badge2 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

you need to add

camera.rotation.order = 'YXZ' // default 'XYZ'

in your init() function

reason: rotation matrix multiplication order

you can see detalised info here: https://en.wikipedia./wiki/Euler_angles#Rotation_matrix
and also here: https://en.wikipedia./wiki/Rotation_matrix#In_three_dimensions

also if you add this:

const axesHelper = new THREE.AxesHelper( 15 )
//axesHelper.renderOrder = 1;
axesHelper.material.depthTest = false;
scene.add( axesHelper )

in init() it will show world axes (and will be always on top of all objects) i needed that while cheking your code so maybe you also will need it in future

本文标签: javascriptHow to fix ThreeJs camera controls only updating for starting viewStack Overflow