You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

THREE.JS FPS视角旋转异常:相机卡顿并反向旋转问题求助

THREE.js FPS视角旋转卡顿并反向旋转问题

在实现第一人称射击(FPS)视角旋转系统时,相机在特定旋转角度(通过console.log查看this.rotation.y约为1/-1)会出现短暂卡顿,随后反向旋转,现象异常怪异。

game.js代码

//import 3d
import * as engine from './three/src/Three.js'
//import GLTF Loader
import {GLTFLoader} from './three/examples/jsm/loaders/GLTFLoader.js'
import {OBJLoader} from './three/examples/jsm/loaders/OBJLoader.js'
import {MTLLoader} from './three/examples/jsm/loaders/MTLLoader.js'

//import Player
import {Player} from './src/player.js'

//make player
const player = new Player(0.1)

//Make pause variable
var isPaused = false;

//make camera
const camera = new engine.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
//change camera position
player.position.z = 3;

//create scene
const scene = new engine.Scene();
//put skyblue background
scene.background = new engine.Color("skyblue")

//create renderer
const renderer = new engine.WebGLRenderer();
//set renderer size
renderer.setSize(window.innerWidth, window.innerHeight);
//set renderer animation
renderer.setAnimationLoop(animate)
//set renderer.domElement style and attributes
renderer.domElement.style.width = "100%";
renderer.domElement.style.height = "100%";
renderer.domElement.className = "main";
renderer.domElement.id = "main"

//light:
const ambientLight = new engine.AmbientLight(0xFFFFFF);
ambientLight.intensity = 4;
scene.add( ambientLight );

//game logic: 

//camera:
//ask for pointer lock
renderer.domElement.addEventListener("click", () => {
    renderer.domElement.requestPointerLock();
})
//Rotate the camera when mouse moving
renderer.domElement.addEventListener("mousemove", player.handleMouseMove)

//Make something when pointer lock
document.addEventListener("pointerlockchange", () => {
    if (document.pointerLockElement === renderer.domElement) {
        isPaused = false;
    } else {
        isPaused = true;
    }
})

setInterval(() => {

    if (isPaused) {
        player.needToHandleMouseMove = false;
        player.needToHandleKeyboard = false;
        document.getElementById("pauseMenu").style.display = "";
    } else {
        player.needToHandleMouseMove = true;
        player.needToHandleKeyboard = true;
        document.getElementById("pauseMenu").style.display = "none";
    }
}, 20)
//Make paused in start
isPaused = true;

// END OF CAMERA
//Movement:
document.addEventListener("keydown", player.handleKeyDown)
document.addEventListener("keyup", player.handleKeyUp)
//END OF MOVEMENT
//disable scrolling
document.addEventListener('wheel', (event) => {event.preventDefault();}, {passive: false});

//append the game
document.body.appendChild(renderer.domElement);

//name all laoders
const glftLoader = new GLTFLoader();
const objLoader = new OBJLoader();
const mtlLoader = new MTLLoader();
//load my testmap
glftLoader.load("./res/TestMap.gltf", (glftScene) => {
    var obj = glftScene.scene.children[0];
    obj.position.x = 0;
    obj.position.y = 0;
    obj.position.z = 0;
    scene.add(obj)
})


function animate(time) {

    player.checkForMovement();


    camera.rotation.x = player.rotation.x;
    camera.rotation.y = player.rotation.y;
    camera.rotation.z = player.rotation.z;

    camera.position.x = player.position.x;
    camera.position.y = player.position.y;
    camera.position.z = player.position.z;

    renderer.render(scene, camera);
};

//HTML Logic:
document.getElementById("play").addEventListener("click", () => {isPaused = !isPaused; renderer.domElement.requestPointerLock()})
document.getElementById("menuquit").addEventListener("click", () => {window.location.href = "../../menus/index/index.html"})
document.getElementById("osquit").addEventListener("click", () => {require("electron").ipcRenderer.sendSync("closed", "close")})

src/Player.js代码

import * as THREE from '../three/src/Three.js'

class Player {
    constructor(moveSpeed, keys = { W: false, A: false, S: false, D: false, ' ': false, SHIFT: false }) {
        this.moveSpeed = moveSpeed;
        this.keys = keys;
        this.position = new THREE.Vector3();
        this.rotation = new THREE.Quaternion();
        this.needToHandleMouseMove = false;
        this.needToHandleKeyboard = false;

        // Bind event handlers to the instance
        this.handleKeyDown = this.handleKeyDown.bind(this);
        this.handleKeyUp = this.handleKeyUp.bind(this);
        this.handleMouseMove = this.handleMouseMove.bind(this);
    }

    // Handle Key Down. PUT IN document.addEventListener("keydown")
    handleKeyDown(event) {
        if (this.needToHandleKeyboard) {
            const key = event.key.toUpperCase();
            console.log("Key: " + key)
            if (this.keys.hasOwnProperty(key)) {
                this.keys[key] = true;
                if (key === ' ') {
                    event.preventDefault();
                }
            }
        }
    }

    // Handle Key Up. PUT IN document.addEventListener("keyup")
    handleKeyUp(event) {
        if (this.needToHandleKeyboard) {
            const key = event.key.toUpperCase();
            console.log("KeyUp: " + key)
            if (this.keys.hasOwnProperty(key)) {
                this.keys[key] = false;
            }
        }
    }

    // Handle Mouse move. PUT IN document.addEventListener("mousemove")
    handleMouseMove(event) {
        if (this.needToHandleMouseMove) {
            let movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
            let movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
            let quaternionY = new THREE.Quaternion();
            quaternionY.setFromAxisAngle(new THREE.Vector3(0, 1, 0), -movementX * 0.002);
            
            this.rotation.multiply(quaternionY);
          
            /* let quaternionX = new THREE.Quaternion();
            quaternionX.setFromAxisAngle(new THREE.Vector3(1, 0, 0), -movementY * 0.002);
         
            let newRotation = this.rotation.clone().multiply(quaternionX);
         
            let angle = new THREE.Euler().setFromQuaternion(newRotation, 'YXZ').x;
          
            if (angle > -Math.PI / 2 && angle < Math.PI / 2) {
              this.rotation.copy(newRotation);
            } */
        }
    }

    // Move the player with keys. PUT THAT IN ANIMATION LOOP
    checkForMovement() {
        let moveSpeedX = this.moveSpeed * Math.sin(this.rotation.y);
        let moveSpeedZ = this.moveSpeed * Math.cos(this.rotation.y);

        if (this.keys.W) {
            this.position.x -= moveSpeedX;
            this.position.z -= moveSpeedZ;
        }
        if (this.keys.S) {
            this.position.x += moveSpeedX;
            this.position.z += moveSpeedZ;
        }
        if (this.keys.D) {
            this.position.x += moveSpeedZ;
            this.position.z -= moveSpeedX;
        }
        if (this.keys.A) {
            this.position.x -= moveSpeedZ;
            this.position.z += moveSpeedX;
        }
        if (this.keys[' ']) this.position.y += this.moveSpeed;
        if (this.keys.SHIFT) this.position.y -= this.moveSpeed;
    }
}
export { Player };

内容的提问来源于stack exchange,提问作者Paolog

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 07:12:06