threejs 第一人称视角,指针锁定控制器PointerLockControls的使用
官方Demo
官方的使用例子 three.js - pointerlock controls
官方文档 PointerLockControls – three.js docs (threejs.org)
项目中遇到的问题
- 在用键盘WASD来控制,视角的移动时候,视角的方向一直是在一个点上。
问题原因: 是因为页面中有其他的 controls 控制器一直在 update() 引起的。
解决办法: 找到项目(页面)中所有的 controls.update(),在lock()时候关闭。
代码
官方的的例子在Y轴有重力,下面基于例子修改了一个没有重力的使用情况。
源码:
import * as THREE from 'three';
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
export class FirstPerson {
constructor(camera, renderer) {
this.scene = scene;
this.camera = camera;
this.renderer = renderer;
this.moveForward = false;
this.moveLeft = false;
this.moveBackward = false;
this.moveRight = false;
this.moveUp = false;
this.moveBottom = false;
this.prevTime = performance.now();
this.velocity = new THREE.Vector3();
this.direction = new THREE.Vector3();
this.controls = new PointerLockControls(camera, document.body);
this.attachEvent();
}
lock() {
this.controls.lock();
}
attachEvent() {
let context = this;
document.addEventListener('click', () => {
this.lock();
})
const onKeyDown = function (event) {
switch (event.code) {
case 'ArrowUp':
case 'KeyW':
context.moveForward = true;
break;
case 'ArrowLeft':
case 'KeyA':
context.moveLeft = true;
break;
case 'ArrowDown':
case 'KeyS':
context.moveBackward = true;
break;
case 'ArrowRight':
case 'KeyD':
context.moveRight = true;
break;
case 'KeyR':
case 'Space':
context.moveUp = true;
break;
case 'KeyF':
case 'KeyB':
context.moveBottom = true;
}
};
const onKeyUp = function (event) {
switch (event.code) {
case 'ArrowUp':
case 'KeyW':
context.moveForward = false;
break;
case 'ArrowLeft':
case 'KeyA':
context.moveLeft = false;
break;
case 'ArrowDown':
case 'KeyS':
context.moveBackward = false;
break;
case 'ArrowRight':
case 'KeyD':
context.moveRight = false;
break;
case 'KeyR':
case 'Space':
context.moveUp = false;
break;
case 'KeyF':
case 'KeyB':
context.moveBottom = false;
}
};
document.addEventListener('keydown', onKeyDown);
document.addEventListener('keyup', onKeyUp);
}
update() {
const time = performance.now();
if (this.controls.isLocked === true) {
const delta = (time - this.prevTime) / 1000;
this.velocity.x -= this.velocity.x * 10.0 * delta;
this.velocity.y -= this.velocity.y * 10.0 * delta;
this.velocity.z -= this.velocity.z * 10.0 * delta;
this.direction.z = Number(this.moveForward) - Number(this.moveBackward);
this.direction.y = Number(this.moveUp) - Number(this.moveBottom);
this.direction.x = Number(this.moveRight) - Number(this.moveLeft);
this.direction.normalize();
if (this.moveForward || this.moveBackward) this.velocity.z -= this.direction.z * 400.0 * delta;
if (this.moveUp || this.moveBottom) this.velocity.y -= this.direction.y * 400.0 * delta;
if (this.moveLeft || this.moveRight) this.velocity.x -= this.direction.x * 400.0 * delta;
this.controls.moveRight(- this.velocity.x * delta);
this.controls.moveForward(- this.velocity.z * delta);
this.controls.getObject().position.y -= ( this.velocity.y * delta );
}
this.prevTime = time;
}
}
使用:
// ......
let pfControls = new FirstPerson(camera, renderer);
function animation(time) {
renderer.render(scene, camera);
pfControls.update();
}
收获
在鼠标点击页面进入lock状态时候,怎么计算控制camera的旋转。
PointerLockControls里面的实现过程。
function onMouseMove( event ) {
if ( scope.isLocked === false ) return;
const movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
const movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
_euler.setFromQuaternion( camera.quaternion );
_euler.y -= movementX * 0.002 * scope.pointerSpeed;
_euler.x -= movementY * 0.002 * scope.pointerSpeed;
_euler.x = Math.max( _PI_2 - scope.maxPolarAngle, Math.min( _PI_2 - scope.minPolarAngle, _euler.x ) );
camera.quaternion.setFromEuler( _euler );
scope.dispatchEvent( _changeEvent );
}
- event.movementX 和 event.movementY 表示与上一个mousemove事件之间的距离,根据 movementX 来计算选要旋转的角度值。
- Euler欧拉角的使用。
- 最后再设置 camera 的旋转值。
简单点的控制camera旋转
document.body.addEventListener( 'mousemove', ( event ) => {
if ( document.pointerLockElement === document.body ) {
camera.rotation.y -= event.movementX / 500;
camera.rotation.x -= event.movementY / 500;
}
} );
欧拉角是什么?
欧拉角是一种常用的旋转表示方法,用于描述物体在三维空间中的旋转状态。它通过三个角度来表示物体绕三个坐标轴的旋转。
通常,欧拉角使用三个角度来表示旋转,分别称为俯仰角(Pitch)、偏航角(Yaw)和滚转角(Roll)。
俯仰角(Pitch):绕物体的 X 轴旋转。
偏航角(Yaw):绕物体的 Y 轴旋转。
滚转角(Roll):绕物体的 Z 轴旋转。
这三个角度可以以不同的顺序组合,形成不同的旋转顺序。例如,当旋转顺序为 "XYZ" 时,先进行俯仰角旋转,然后是偏航角旋转,最后是滚转角旋转。而当旋转顺序为 "YXZ" 时,先进行偏航角旋转,然后是俯仰角旋转,最后是滚转角旋转。