如何用Three.js的PointerLockControls实现第三人称视角控制?
问题描述
我想基于Three.js制作第三人称游戏,类似某Demo的效果。旧版Three.js中,可以将Object3D作为锚点绑定相机后传入PointerLockControls,实现场景围绕角色旋转;但新版PointerLockControls构造函数只能传入相机,我当前的代码导致场景围绕相机旋转,尝试移动相机、组合相机与模型等方法都没解决,想知道如何用新版PointerLockControls实现围绕角色旋转的第三人称控制效果。
旧版Demo代码:
let anchor = new THREE.Object3D() // anchor = character anchor.position.y = 10 // 此处定义了一个透视相机 anchor.add(camera) // 将相机挂载到锚点上 controls = new PointerLockControls(anchor, container)
我的当前代码:
// 此处定义了一个透视相机 camera.position.set(4, 4, 0) const webglContainer = document.getElementById('webgl-container') if (webglContainer) { const controls = new PointerLockControls(camera, webglContainer) scene.add(controls.getObject()) webglContainer.addEventListener('click', () => { controls.lock() }) }
解决方案
核心思路是让角色对象作为旋转锚点,相机作为角色的子对象跟随旋转,同时让PointerLockControls的旋转逻辑作用于角色而非直接作用于相机。具体实现步骤:
- 创建角色对象(作为锚点),将相机添加为角色的子对象,设置相机相对于角色的固定偏移(比如后上方的第三人称视角位置)。
- 初始化PointerLockControls时仍传入相机,但不要将
controls.getObject()添加到场景,而是将角色对象添加到场景。 - 监听PointerLockControls的
change事件,将相机的旋转同步到角色对象上(只同步Y轴旋转,避免相机俯仰影响角色朝向)。 - 处理角色移动时,基于角色的当前朝向计算移动方向,确保移动逻辑符合第三人称视角的预期。
完整代码示例
// 1. 定义基础场景、相机、渲染器 const scene = new THREE.Scene() const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000) const renderer = new THREE.WebGLRenderer({ antialias: true }) renderer.setSize(window.innerWidth, window.innerHeight) document.body.appendChild(renderer.domElement) // 2. 创建角色对象(作为旋转锚点) const character = new THREE.Object3D() character.position.y = 1 // 角色基础高度 scene.add(character) // 3. 将相机挂载到角色上,设置第三人称视角偏移 camera.position.set(0, 2, -5) // 相机在角色后上方 camera.lookAt(character.position) // 相机始终看向角色 character.add(camera) // 4. 初始化PointerLockControls const webglContainer = renderer.domElement const controls = new THREE.PointerLockControls(camera, webglContainer) // 5. 同步相机旋转到角色(仅Y轴,保持角色水平旋转) let prevCameraRotationY = 0 controls.addEventListener('change', () => { const rotationDelta = camera.rotation.y - prevCameraRotationY character.rotation.y += rotationDelta prevCameraRotationY = camera.rotation.y // 重置相机自身的Y轴旋转,避免累积 camera.rotation.y = 0 }) // 6. 角色移动逻辑(示例) const moveSpeed = 0.1 const keys = {} document.addEventListener('keydown', (e) => keys[e.code] = true) document.addEventListener('keyup', (e) => keys[e.code] = false) function updateCharacterMovement() { if (!controls.isLocked) return const direction = new THREE.Vector3() // 基于角色朝向计算移动方向 if (keys['KeyW']) direction.z -= 1 if (keys['KeyS']) direction.z += 1 if (keys['KeyA']) direction.x -= 1 if (keys['KeyD']) direction.x += 1 direction.normalize().multiplyScalar(moveSpeed) // 将方向转换为世界坐标系下的移动 character.translateOnAxis(direction, 1) } // 7. 渲染循环 function animate() { requestAnimationFrame(animate) updateCharacterMovement() renderer.render(scene, camera) } animate() // 8. 锁定/解锁控制 webglContainer.addEventListener('click', () => { if (controls.isLocked) { controls.unlock() } else { controls.lock() } })
关键说明
- 角色对象作为旋转的核心,相机挂载在其下,所以旋转角色时相机会自然围绕角色转动。
- 仅同步相机的Y轴旋转到角色,避免相机的俯仰(X轴旋转)影响角色的朝向,保持第三人称视角的合理性。
- 移动逻辑基于角色的当前朝向,确保WASD移动是相对于角色的前方/后方/左右,符合玩家预期。
内容的提问来源于stack exchange,提问作者Prav
相关产品推荐
相关产品推荐

