Three.js实现漫步效果
Three.js实现漫步效果
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./assets/css/style.css">
</head>
<body>
<div id="blocker">
<div id="instructions">
<p style="font-size:36px">
Click to play
</p>
<p>
Move: WASD<br/>
Look: MOUSE
</p>
</div>
</div>
<script src="./main/main.js"></script>
</body>
</html>
main.js:
import * as THREE from "three";
import { PointerLockControls } from "three/examples/jsm/controls/PointerLockControls";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import testGlb from "../assets/glbs/test3.glb";
// 定义照相机、场景、渲染器、控制器
let camera, scene, renderer, controls;
// 定义四个方向
let moveForward = false;
let moveBackward = false;
let moveLeft = false;
let moveRight = false;
// 定义时间
let prevTime = performance.now();
// 定义速度向量
const velocity = new THREE.Vector3();
// 定义方向向量
const direction = new THREE.Vector3();
// 创建场景
scene = new THREE.Scene();
// 设置摄像机
camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
2000
);
camera.position.z = 0;
camera.position.y = 1;
camera.position.x = 0;
camera.lookAt(0, 1, 0);
// 创建渲染器
renderer = new THREE.WebGLRenderer({
antialias: true,
// 设置对数深度缓冲区
logarithmicDepthBuffer: true,
depthTest: false
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(new THREE.Color(0xeeeeee));
document.body.appendChild(renderer.domElement);
// 添加指针锁定控制器、用于第一人称的3D
controls = new PointerLockControls(camera, document.body);
const blocker = document.getElementById("blocker");
const instructions = document.getElementById("instructions");
instructions.addEventListener("click", function () {
controls.lock();
});
controls.addEventListener("lock", function () {
instructions.style.display = "none";
blocker.style.display = "none";
});
controls.addEventListener("unlock", function () {
blocker.style.display = "block";
instructions.style.display = "";
});
scene.add(controls.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;
}
};
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);
// 加载3D模型
const loader = new GLTFLoader();
loader.load(testGlb, (gltf) => {
gltf.scene.position.set(0, 0, 0);
scene.add(gltf.scene);
});
// 设置灯光
var spotLight = new THREE.SpotLight();
spotLight.position.set(30, 30, 30);
spotLight.castShadow = true;
scene.add(spotLight);
// 封装渲染函数
function animate() {
// 请求动画帧
requestAnimationFrame(animate);
const time = performance.now();
if (controls.isLocked === true) {
const delta = (time - prevTime) / 1000; // 约等于0.05
velocity.x -= velocity.x * 10.0 * delta;
velocity.z -= velocity.z * 10.0 * delta;
velocity.y -= 9.8 * 10.0 * delta;
// 控制水平方向上的移动
direction.x = Number(moveRight) - Number(moveLeft);
// 控制垂直方向上的移动
direction.z = Number(moveForward) - Number(moveBackward);
direction.normalize();
if (moveForward || moveBackward) velocity.z -= direction.z * 50.0 * delta;
if (moveLeft || moveRight) velocity.x -= direction.x * 50.0 * delta;
// 调用指针控制器的API来调整相机的位置
controls.moveRight(- velocity.x * delta);
controls.moveForward(- velocity.z * delta);
velocity.y = Math.max(0, velocity.y);
controls.getObject().position.y += (velocity.y * delta);
if (controls.getObject().position.y < 10) {
velocity.y = 0;
controls.getObject().position.y = 1;
}
}
prevTime = time;
renderer.render(scene, camera);
}
// 监听页面尺寸的变化
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
renderer.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener('resize', onWindowResize);
animate();