three.js第三人称控制类代码
发布时间:
2023-09-22
预览次数:
以下是一个基本的three.js第三人称控制类代码的示例:
class ThirdPersonControls { constructor(camera, target, options = {}) { this.camera = camera; this.target = target; this.enabled = true; this.distance = options.distance || 10; this.minDistance = options.minDistance || 0.1; this.maxDistance = options.maxDistance || Infinity; this.minPolarAngle = options.minPolarAngle || 0; // 最小极角 this.maxPolarAngle = options.maxPolarAngle || Math.PI; // 最大极角 this.minAzimuthAngle = options.minAzimuthAngle || -Infinity; // 最小方位角 this.maxAzimuthAngle = options.maxAzimuthAngle || Infinity; // 最大方位角 this.rotateSpeed = options.rotateSpeed || 1; this.zoomSpeed = options.zoomSpeed || 1; this.panSpeed = options.panSpeed || 1; this.enableRotate = options.enableRotate !== undefined ? options.enableRotate : true; this.enableZoom = options.enableZoom !== undefined ? options.enableZoom : true; this.enablePan = options.enablePan !== undefined ? options.enablePan : true; this.keys = options.keys || { LEFT: 37, // 左箭头键 UP: 38, // 上箭头键 RIGHT: 39, // 右箭头键 BOTTOM: 40 // 下箭头键 }; this.mouseButtons = options.mouseButtons || { ORBIT: THREE.MOUSE.LEFT, // 鼠标左键 ZOOM: THREE.MOUSE.MIDDLE, // 鼠标中键 PAN: THREE.MOUSE.RIGHT // 鼠标右键 }; this.target0 = this.target.clone(); this.position0 = this.camera.position.clone(); this.zoom0 = this.camera.zoom; this.rotateStart = new THREE.Vector2(); this.rotateEnd = new THREE.Vector2(); this.rotateDelta = new THREE.Vector2(); this.panStart = new THREE.Vector2(); this.panEnd = new THREE.Vector2(); this.panDelta = new THREE.Vector2(); this.dollyStart = new THREE.Vector2(); this.dollyEnd = new THREE.Vector2(); this.dollyDelta = new THREE.Vector2(); this.update(); this.domElement.addEventListener('mousedown', this.onMouseDown.bind(this), false); this.domElement.addEventListener('mousewheel', this.onMouseWheel.bind(this), false); this.domElement.addEventListener('DOMMouseScroll', this.onMouseWheel.bind(this), false); // 兼容Firefox this.domElement.addEventListener('mousemove', this.onMouseMove.bind(this), false); this.domElement.addEventListener('mouseup', this.onMouseUp.bind(this), false); this.domElement.addEventListener('mouseout', this.onMouseUp.bind(this), false); this.domElement.addEventListener('keydown', this.onKeyDown.bind(this), false); this.domElement.addEventListener('keyup', this.onKeyUp.bind(this), false); } update() { const offset = new THREE.Vector3(); const quat = new THREE.Quaternion().setFromUnitVectors( this.camera.up, new THREE.Vector3(0, 1, 0) ); const quatInverse = quat.clone().inverse(); const lastPosition = new THREE.Vector3(); const lastQuaternion = new THREE.Quaternion(); return function update() { const position = this.camera.position; offset.copy(position).sub(this.target); offset.applyQuaternion(quat); // 方位角和极角 const azimuthAngle = Math.atan2(offset.x, offset.z); const polarAngle = Math.acos(Math.max(-1, Math.min(1, offset.y / this.distance))); azimuthAngle += this.rotateDelta.x * this.rotateSpeed; polarAngle += this.rotateDelta.y * this.rotateSpeed; polarAngle = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, polarAngle)); azimuthAngle = Math.max(this.minAzimuthAngle, Math.min(this.maxAzimuthAngle, azimuthAngle)); const radius = offset.length() * Math.cos(polarAngle); offset.x = radius * Math.sin(polarAngle) * Math.sin(azimuthAngle); offset.y = radius * Math.cos(polarAngle); offset.z = radius * Math.sin(polarAngle) * Math.cos(azimuthAngle); offset.applyQuaternion(quatInverse); position.copy(this.target).add(offset); this.camera.lookAt(this.target); this.rotateDelta.set(0, 0); }; } onMouseDown(event) { if (!this.enabled) return; event.preventDefault(); if (event.button === this.mouseButtons.ORBIT) { if (this.enableRotate === false) return; this.rotateStart.set(event.clientX, event.clientY); } else if (event.button === this.mouseButtons.ZOOM) { if (this.enableZoom === false) return; this.dollyStart.set(event.clientX, event.clientY); } else if (event.button === this.mouseButtons.PAN) { if (this.enablePan === false) return; this.panStart.set(event.clientX, event.clientY); } this.domElement.addEventListener('mousemove', this.onMouseMove.bind(this), false); this.domElement.addEventListener('mouseup', this.onMouseUp.bind(this), false); this.domElement.addEventListener('mouseout', this.onMouseUp.bind(this), false); } onMouseMove(event) { if (!this.enabled) return; event.preventDefault(); if (event.button === this.mouseButtons.ORBIT) { if (this.enableRotate === false) return; this.rotateEnd.set(event.clientX, event.clientY); this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart); this.rotateStart.copy(this.rotateEnd); } else if (event.button === this.mouseButtons.ZOOM) { if (this.enableZoom === false) return; this.dollyEnd.set(event.clientX, event.clientY); this.dollyDelta.subVectors(this.dollyEnd, this.dollyStart); if (this.dollyDelta.y > 0) { this.zoomIn(); } else if (this.dollyDelta.y < 0) { this.zoomOut(); } this.dollyStart.copy(this.dollyEnd); } else if (event.button === this.mouseButtons.PAN) { if (this.enablePan === false) return; this.panEnd.set(event.clientX, event.clientY); this.panDelta.subVectors(this.panEnd, this.panStart); this.pan(this.panDelta.x, this.panDelta.y); this.panStart.copy(this.panEnd); } } onMouseUp(event) { if (!this.enabled) return; event.preventDefault(); this.domElement.removeEventListener('mousemove', this.onMouseMove.bind(this), false); this.domElement.removeEventListener('mouseup', this.onMouseUp.bind(this), false); this.domElement.removeEventListener('mouseout', this.onMouseUp.bind(this), false); } onMouseWheel(event) { if (!this.enabled || this.enableZoom === false || (this.state !== STATE.NONE && this.state !== STATE.ROTATE)) return; event.preventDefault(); event.stopPropagation(); const delta = 0; if (event.wheelDelta !== undefined) { delta = event.wheelDelta; } else if (event.detail !== undefined) { delta = -event.detail; } if (delta > 0) { this.zoomOut(); } else if (delta < 0) { this.zoomIn(); } } onKeyDown(event) { if (!this.enabled || !this.enableKeys || !this.enablePan) return; switch (event.keyCode) { case this.keys.UP: this.pan(0, this.panSpeed); break; case this.keys.BOTTOM: this.pan(0, -this.panSpeed); break; case this.keys.LEFT: this.pan(this.panSpeed, 0); break; case this.keys.RIGHT: this.pan(-this.panSpeed, 0); break; } } onKeyUp(event) { if (!this.enabled || !this.enableKeys || !this.enablePan) return; switch (event.keyCode) { case this.keys.UP: case this.keys.BOTTOM: this.pan(0, 0); break; case this.keys.LEFT: case this.keys.RIGHT: this.pan(0, 0); break; } } zoomIn() { const factor = Math.pow(0.95, this.zoomSpeed); if (this.camera.isPerspectiveCamera) { this.distance *= factor; this.distance = Math.max(this.minDistance, Math.min(this.maxDistance, this.distance)); } else if (this.camera.isOrthographicCamera) { this.camera.zoom = Math.max(this.minZoom, Math.min(this.maxZoom, this.camera.zoom * factor)); this.camera.updateProjectionMatrix(); } } zoomOut() { const factor = Math.pow(0.95, this.zoomSpeed); if (this.camera.isPerspectiveCamera) { this.distance /= factor; this.distance = Math.max(this.minDistance, Math.min(this.maxDistance, this.distance)); } else if (this.camera.isOrthographicCamera) { this.camera.zoom = Math.max(this.minZoom, Math.min(this.maxZoom, this.camera.zoom / factor)); this.camera.updateProjectionMatrix(); } } pan(deltaX, deltaY) { const offset = new THREE.Vector3(); const te = this.camera.matrix.elements; offset.set( te[0] * deltaX + te[4] * deltaY, te[1] * deltaX + te[5] * deltaY, te[2] * deltaX + te[6] * deltaY ); this.target.add(offset); this.camera.position.add(offset); } } 在使用该控制类时,你需要传入相机对象和目标对象,并可以通过options参数来设置一些控制选项。例如: 复制代码 const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const target = new THREE.Vector3(0, 0, 0); const controls = new ThirdPersonControls(camera, target, { distance: 10, minDistance: 0.1, maxDistance: 20, minPolarAngle: 0, maxPolarAngle: Math.PI, minAzimuthAngle: -Infinity, maxAzimuthAngle: Infinity, rotateSpeed: 1, zoomSpeed: 1, panSpeed: 1, enableRotate: true, enableZoom: true, enablePan: true, keys: { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }, mouseButtons: { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT } }); function animate() { requestAnimationFrame(animate); controls.update(); renderer.render(scene, camera); } 这样,你就可以通过鼠标和键盘来控制相机的位置和视角了。