three.js添加人物实现第一人称视角控制,类似于cf或绝地求生游戏控制人物

three.js实现第一人称视角控制

思路

查看官方例子,使用PointerLockControls可以实现该功能,官方参考地址
然后可以将模型加载进来同步修改位置和观察方向就可以了

具体代码如下

<!DOCTYPE html>
<html lang="en">
	<head>
		<title>three.js - pointerlock controls</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<link type="text/css" rel="stylesheet" href="main.css">
		<style>
			#blocker {
				position: absolute;
				width: 100%;
				height: 100%;
				background-color: rgba(0,0,0,0.5);
			}

			#instructions {
				width: 100%;
				height: 100%;

				display: flex;
				flex-direction: column;
				justify-content: center;
				align-items: center;

				text-align: center;
				font-size: 14px;
				cursor: pointer;
			}
		</style>
	</head>
	<body>
		<div id="blocker">
			<div id="instructions">
				<p style="font-size:36px">
					Click to play
				</p>
				<p>
					Move: WASD<br/>
					Jump: SPACE<br/>
					Look: MOUSE
				</p>
			</div>
		</div>

		<script type="module">

			import * as THREE from '../build/three.module.js';

			import { PointerLockControls } from './jsm/controls/PointerLockControls.js';
			import { GLTFLoader } from './jsm/loaders/GLTFLoader.js';

			let camera, scene, renderer, controls;

			const objects = [];

			let raycaster;

			let moveForward = false;
			let moveBackward = false;
			let moveLeft = false;
			let moveRight = false;
			let canJump = false;
			//根据navigation属性,开始计算毫秒数,通过两次时间相减可以计算某个操作的间隔时间
			let prevTime = performance.now();
			const velocity = new THREE.Vector3();
			const direction = new THREE.Vector3();
			const vertex = new THREE.Vector3();
			const color = new THREE.Color();
			let person=null,mixer,stateList={},currentAction,previousAction,lastkey
			let clock = new THREE.Clock();
			init();
			animate();

			function init() {

				camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1000 );
				camera.position.set(1,200,1);

				scene = new THREE.Scene();
				scene.background = new THREE.Color( 0xffffff );
				scene.fog = new THREE.Fog( 0xffffff, 0, 750 );

				const light = new THREE.HemisphereLight( 0xeeeeff, 0x777788, 0.75 );
				light.position.set( 0.5, 1, 0.75 );
				scene.add( light );

				controls = new PointerLockControls( camera, document.body );

				const blocker = document.getElementById( 'blocker' );
				const instructions = document.getElementById( 'instructions' );

				instructions.addEventListener( 'click', function () {
					camera.position.set(1,10,1);
					camera.lookAt(new THREE.Vector3(1,1,1));
					controls.lock();

				} );

				controls.addEventListener( 'lock', function () {
					//camera.position.y = 1;
					instructions.style.display = 'none';
					blocker.style.display = 'none';

				} );

				controls.addEventListener( 'unlock', function () {

					blocker.style.display = 'block';
					instructions.style.display = '';

				} );
				controls.addEventListener( 'change', function (evt) {

					//console.log('change',evt)
					let dir1=new THREE.Vector3()
					controls.getDirection(dir1)
					console.log('change getDirection',dir1)
					console.log('change position',controls.getObject().position)

				} );
				scene.add( controls.getObject() );

				const onKeyDown = function ( event ) {
					if (lastkey !== event.code) {
						lastkey = event.code;
						fadeToAction('Walking', 0.2);
					}
					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 ) {
					lastkey = null;
					fadeToAction('Standing', 0.2);

					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 );

				// floor

				let floorGeometry = new THREE.PlaneGeometry( 2000, 2000, 100, 100 );
				floorGeometry.rotateX( - Math.PI / 2 );

				// vertex displacement

				let position = floorGeometry.attributes.position;

				for ( let i = 0, l = position.count; i < l; i ++ ) {

					vertex.fromBufferAttribute( position, i );

					vertex.x += Math.random() * 20 - 10;
					vertex.y += Math.random() * 2;
					vertex.z += Math.random() * 20 - 10;

					position.setXYZ( i, vertex.x, vertex.y, vertex.z );

				}

				floorGeometry = floorGeometry.toNonIndexed(); // ensure each face has unique vertices

				position = floorGeometry.attributes.position;
				const colorsFloor = [];
				for ( let i = 0, l = position.count; i < l; i ++ ) {

					color.setHSL( Math.random() * 0.3 + 0.5, 0.75, Math.random() * 0.25 + 0.75 );
					colorsFloor.push( color.r, color.g, color.b );

				}

				floorGeometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colorsFloor, 3 ) );

				const floorMaterial = new THREE.MeshBasicMaterial( { vertexColors: true } );

				const floor = new THREE.Mesh( floorGeometry, floorMaterial );
				scene.add( floor );

				// objects

				const boxGeometry = new THREE.BoxGeometry( 20, 20, 20 ).toNonIndexed();

				position = boxGeometry.attributes.position;
				const colorsBox = [];

				for ( let i = 0, l = position.count; i < l; i ++ ) {

					color.setHSL( Math.random() * 0.3 + 0.5, 0.75, Math.random() * 0.25 + 0.75 );
					colorsBox.push( color.r, color.g, color.b );

				}

				boxGeometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colorsBox, 3 ) );

				for ( let i = 0; i < 500; i ++ ) {

					const boxMaterial = new THREE.MeshPhongMaterial( { specular: 0xffffff, flatShading: true, vertexColors: true } );
					boxMaterial.color.setHSL( Math.random() * 0.2 + 0.5, 0.75, Math.random() * 0.25 + 0.75 );

					const box = new THREE.Mesh( boxGeometry, boxMaterial );
					box.position.x = Math.floor( Math.random() * 20 - 10 ) * 20;
					box.position.y = Math.floor( Math.random() * 20 ) * 20 + 10;
					box.position.z = Math.floor( Math.random() * 20 - 10 ) * 20;

					scene.add( box );
					objects.push( box );

				}
				// 添加辅助
				var axesHelper = new THREE.AxesHelper(30);
				scene.add(axesHelper);


				// 添加人物
				const gltfloader=new GLTFLoader()
				let modelConfig={
					url:'models/gltf/RobotExpressive/RobotExpressive.glb',
					scale:1,
					position:[0,0,0],
					openAnimate:true,
				}
				gltfloader.load( modelConfig.url,  ( gltf )=> {
					person=gltf.scene
					if(modelConfig.position){
						gltf.scene.position.set(modelConfig.position[0], modelConfig.position[1], modelConfig.position[2])
					}
					if(modelConfig.scale){
						gltf.scene.scale.set(modelConfig.scale, modelConfig.scale, modelConfig.scale)
					}
					if(modelConfig.rotateX){
						gltf.scene.rotateX(modelConfig.rotateX)
					}
					if(modelConfig.rotateY){
						gltf.scene.rotateX(modelConfig.rotateY)
					}
					if(modelConfig.rotateZ){
						gltf.scene.rotateX(modelConfig.rotateZ)
					}
					mixer = new THREE.AnimationMixer(person);
					stateList.Walking = mixer.clipAction(gltf.animations[10]);
					stateList.Standing = mixer.clipAction(gltf.animations[7]);
					// 设置下面两项主要是站着的时候,别抖了
					stateList.Standing.clampWhenFinished = true;
					stateList.Standing.loop = THREE.LoopOnce;

					currentAction = stateList.Standing;
					currentAction.play();
					scene.add( gltf.scene );
					//person=gltf.scene
				},null,(error)=>{
					console.error(error)
				} );

				renderer = new THREE.WebGLRenderer( { antialias: true } );
				renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( window.innerWidth, window.innerHeight );
				document.body.appendChild( renderer.domElement );

				//

				window.addEventListener( 'resize', onWindowResize );

			}

			function onWindowResize() {

				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();

				renderer.setSize( window.innerWidth, window.innerHeight );

			}

			function animate() {

				requestAnimationFrame( animate );

				const time = performance.now();

				if ( controls.isLocked === true ) {

					raycaster.ray.origin.copy( controls.getObject().position );
					raycaster.ray.origin.y -= 10;

					const intersections = raycaster.intersectObjects( objects, false );

					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;

					}

					controls.moveRight( - velocity.x * delta );
					controls.moveForward( - velocity.z * delta );

					controls.getObject().position.y += ( velocity.y * delta ); // new behavior

					if ( controls.getObject().position.y < 10 ) {

						velocity.y = 0;
						controls.getObject().position.y = 10;

						canJump = true;

					}
					if(person){

						// 同步移动人物位置
						let personPosition =camera.position
						person.position.set(personPosition.x,personPosition.y-8,personPosition.z-2)

						// 同步修改人物方向
						let dir1=new THREE.Vector3()
						// todo 注意此处获得的是方向向量,需要添加该处的position才能获得目标位置
						controls.getDirection(dir1)
						let targetPt=person.position.clone()
						// 获取目标位置
						targetPt.add(dir1)
						person.lookAt(targetPt);
					}

				}

				prevTime = time;
				roleAction()
				renderer.render( scene, camera );

			}
			function roleAction() {
				let dt = clock.getDelta();
				if (mixer) mixer.update(dt);
				//handleRoleAction();
			}
			function fadeToAction(name, duration) {
				previousAction = currentAction;
				currentAction = stateList[name];
				if (previousAction !== currentAction) {
					previousAction.fadeOut(duration);
				}
				if (currentAction) {
					currentAction
							.reset()
							.setEffectiveTimeScale(1)
							.setEffectiveWeight(1)
							.fadeIn(duration)
							.play();
				}
			}

		</script>
	</body>
</html>

最终效果

效果

如有不足之处欢迎来指正,交流QQ 1826356125

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于使用 three.js 进行全景漫游,你可以按照以下步骤进行操作: 1. 首先,确保你已经在网页中引入了 three.js 库文件。你可以通过在 HTML 文件中添加以下代码来引入: ```html <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script> ``` 2. 创建一个场景(Scene)和一个相机(Camera)。你可以使用透视相机(PerspectiveCamera)来模拟真实的视角。 ```javascript var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); ``` 3. 创建一个渲染器(Renderer),并将其添加到页面中的某个容器中。 ```javascript var renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); ``` 4. 创建一个全景球体(SphereGeometry),并将其作为场景的一个子对象。 ```javascript var geometry = new THREE.SphereGeometry(500, 60, 40); geometry.scale(-1, 1, 1); // 反转球体的法线,使其内部可见 var texture = new THREE.TextureLoader().load('your-panorama-image.jpg'); var material = new THREE.MeshBasicMaterial({ map: texture }); var sphere = new THREE.Mesh(geometry, material); scene.add(sphere); ``` 5. 设置相机的位置和目标点。 ```javascript camera.position.set(0, 0, 0); camera.lookAt(scene.position); ``` 6. 添加交互控制(Optional)。你可以使用一些库,如 OrbitControls,来实现用户与全景的交互,例如通过鼠标或触摸移动视角。 ```javascript var controls = new THREE.OrbitControls(camera, renderer.domElement); ``` 7. 渲染场景。 ```javascript function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); } animate(); ``` 通过按照以上步骤进行操作,你就可以在网页中实现基本的 three.js 全景漫游效果了。当然,你还可以根据自己的需求进一步定制和优化。希望对你有帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值