在web端3D的交互中,第一视角漫游(类似于CF游戏射击视角)也是基础功能之一,在three.js的应用中将这个功能拆解一下,无非包括以下几点:
1、锁定摄像机的lookAt方向,让方向跟随鼠标
2、监听键盘,通过更改摄像机的位置模拟进行移动
3、监听空格键的跳跃时,需要进行下落操作
4、不停用渲染器重复渲染,达到平滑视角的目的
下面直接上关键代码(完整代码见附件),静态的html中js有跨域问题,需要使用编辑器打开运行示例
///
///
///第一视角核心代码//
///
///
let pointerLockControls = new THREE.PointerLockControls(camera,document.body);
// 需要锁定的父盒子
let blocker = document.getElementById("content");
// 进行监听的内容盒子
let instructions = document.getElementById("instructions");
//第一人称视角使用变量
const objects = [];
let raycaster;
let moveForward = false;
let moveBackward = false;
let moveLeft = false;
let moveRight = false;
let canJump = false;
let prevTime = performance.now();
const velocity = new THREE.Vector3();
const direction = new THREE.Vector3();
instructions.addEventListener( 'click', function () {
console.log("点击了主屏幕,应该锁定");
//pointerLockControls.lock();
pointerLockControls.idLocked = true;
document.body.requestPointerLock();
});
instructions.addEventListener( 'lock', function (){
instructions.style.display = 'none';
blocker.style.display = 'none';
});
instructions.addEventListener( 'unlock', function (){
pointerLockControls.idLocked = false;
blocker.style.display = 'block';
instructions.style.display = '';
});
scene.add( pointerLockControls.getObject() );
const onKeyDown = function ( event ) {
switch ( event.code ) {
case 'ArrowUp':
case 'KeyW':
moveForward = true;
break;
case 'ArrowLeft':
case 'KeyA':
moveLeft = true;
break;
case 'ArrowDown':
case 'KeyS':
moveBackward = true;
break;
case 'ArrowRight':
case 'KeyD':
moveRight = true;
break;
case 'Space':
if ( canJump === true ) velocity.y += 350;
canJump = false;
break;
}
};
const onKeyUp = function ( event ) {
switch ( event.code ) {
case 'ArrowUp':
case 'KeyW':
moveForward = false;
break;
case 'ArrowLeft':
case 'KeyA':
moveLeft = false;
break;
case 'ArrowDown':
case 'KeyS':
moveBackward = false;
break;
case 'ArrowRight':
case 'KeyD':
moveRight = false;
break;
}
};
document.addEventListener( 'keydown', onKeyDown );
document.addEventListener( 'keyup', onKeyUp );
raycaster = new THREE.Raycaster( new THREE.Vector3(), new THREE.Vector3( 0, - 1, 0 ), 0, 10 );
//窗口变动触发的函数
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
render();
renderer.setSize( window.innerWidth, window.innerHeight );
}
let clock = new THREE.Clock();
function render(){
renderer.clear();
const time = performance.now();
if ( true ) {
console.log("进入动画");
raycaster.ray.origin.copy( pointerLockControls.getObject().position );
raycaster.ray.origin.y -= 10;
const intersections = raycaster.intersectObjects( objects );
const onObject = intersections.length > 0;
const delta = ( time - prevTime ) / 1000;
velocity.x -= velocity.x * 10.0 * delta;
velocity.z -= velocity.z * 10.0 * delta;
velocity.y -= 9.8 * 100.0 * delta; // 100.0 = mass
direction.z = Number( moveForward ) - Number( moveBackward );
direction.x = Number( moveRight ) - Number( moveLeft );
direction.normalize(); // this ensures consistent movements in all directions
if ( moveForward || moveBackward ) velocity.z -= direction.z * 400.0 * delta;
if ( moveLeft || moveRight ) velocity.x -= direction.x * 400.0 * delta;
if ( onObject === true ) {
velocity.y = Math.max( 0, velocity.y );
canJump = true;
}
pointerLockControls.moveRight( - velocity.x * delta );
pointerLockControls.moveForward( - velocity.z * delta );
pointerLockControls.getObject().position.y += ( velocity.y * delta );
if ( pointerLockControls.getObject().position.y < 10 ) {
velocity.y = 0;
pointerLockControls.getObject().position.y = 10;
canJump = true;
}
}
prevTime = time;
requestAnimationFrame(render);
renderer.render(scene,camera);//执行渲染操作
window.onresize = onWindowResize;
}
render();