Three.js自定义PerspectiveCamera子类无法移动位置问题
问题原因及修复方案
核心问题
你的代码里有三个关键问题导致相机位置数值变化但画面毫无反应:
- 位置更新逻辑未加入动画循环:当前
update_position()仅在按键按下时触发一次,按住按键时即使系统重复触发keydown,也不是持续更新的正确方式,必须把位置更新放到每一帧的渲染循环里。 - 未初始化渲染器:
Game类里的this.renderer完全没创建,连渲染画布都没有,自然看不到任何画面变化。 - 相机参数错误:
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
相关产品推荐
相关产品推荐

