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

Three.js自定义PerspectiveCamera子类无法移动位置问题

问题原因及修复方案

核心问题

你的代码里有三个关键问题导致相机位置数值变化但画面毫无反应:

  1. 位置更新逻辑未加入动画循环:当前update_position()仅在按键按下时触发一次,按住按键时即使系统重复触发keydown,也不是持续更新的正确方式,必须把位置更新放到每一帧的渲染循环里。
  2. 未初始化渲染器:Game类里的this.renderer完全没创建,连渲染画布都没有,自然看不到任何画面变化。
  3. 相机参数错误:window.Width和window.Height是不存在的属性,应该用window.innerWidth和window.innerHeight,否则相机宽高比设置错误,可能导致画面显示异常。

修正后的完整代码

class FPS extends THREE.PerspectiveCamera {
    constructor(fov, aspect, near, far) {
        super(fov, aspect, near, far);
        this.keys = {
            forward: false,
            backward: false,
            left: false,
            right: false,
        };
        // 设置相机初始位置,方便观察移动效果
        this.position.set(0, 0, 5);

        window.addEventListener('keydown', (event) => {
            switch (event.keyCode) {
                case 87: // w
                    this.keys.forward = true;
                    break;
                case 65: // a
                    this.keys.left = true;
                    break;
                case 83: // s
                    this.keys.backward = true;
                    break;
                case 68: // d
                    this.keys.right = true;
                    break;
            }
        });

        window.addEventListener('keyup', (event) => {
            switch (event.keyCode) {
                case 87: // w
                    this.keys.forward = false;
                    break;
                case 65: // a
                    this.keys.left = false;
                    break;
                case 83: // s
                    this.keys.backward = false;
                    break;
                case 68: // d
                    this.keys.right = false;
                    break;
            }
        });
    }

    update_position(deltaTime = 1) {
        // 用deltaTime控制移动速度,避免帧率影响移动快慢
        const speed = 5 * deltaTime;
        
        // 基于相机本地坐标系移动,旋转后前后左右依然正确
        const moveDir = new THREE.Vector3();
        if (this.keys.right) moveDir.x += 1;
        if (this.keys.left) moveDir.x -= 1;
        if (this.keys.forward) moveDir.z -= 1; // THREE.js中z轴负方向为向前
        if (this.keys.backward) moveDir.z += 1;

        // 归一化向量,避免斜向移动速度过快
        moveDir.normalize().multiplyScalar(speed);
        this.position.add(moveDir);
    }
}

class Game {
    constructor() {
        this.scene = new THREE.Scene();
        // 修正宽高比参数
        const aspect = window.innerWidth / window.innerHeight;
        this.camera = new FPS(100, aspect, 0.1, 1000);
        
        // 初始化渲染器并添加到页面
        this.renderer = new THREE.WebGLRenderer();
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(this.renderer.domElement);

        // 添加立方体作为参照物,方便直观看到相机移动
        const geometry = new THREE.BoxGeometry(1, 1, 1);
        const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
        const cube = new THREE.Mesh(geometry, material);
        this.scene.add(cube);

        // 记录上一帧时间,用于计算deltaTime
        this.lastTime = performance.now();
        this.render_scene();
    }

    render_scene() {
        const currentTime = performance.now();
        const deltaTime = (currentTime - this.lastTime) / 1000; // 转换为秒单位
        this.lastTime = currentTime;

        // 每帧更新相机位置
        this.camera.update_position(deltaTime);

        this.renderer.render(this.scene, this.camera);
        requestAnimationFrame(() => this.render_scene());
    }
}

// 实例化Game启动应用
const game = new Game();

关键修改说明

  • 动画循环中持续更新位置:把update_position()放到渲染循环里,每帧执行一次,按住按键时相机就能持续移动。
  • 初始化渲染器:创建WebGLRenderer实例,设置尺寸后添加到页面,终于能看到渲染的画面了。
  • 修正相机宽高比:替换错误的window.Width/window.Height为正确的window.innerWidth/window.innerHeight。
  • 基于本地坐标系移动:原来直接修改position.x/z是基于世界坐标系,旋转后方向会混乱,现在用向量计算本地方向,移动逻辑更合理。
  • 添加参照物:绿色立方体作为视觉参考,空场景里根本看不出相机是否移动。
  • deltaTime控制速度:避免不同帧率下移动快慢不一致,保证移动速度稳定。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 19:40:30