반응형

이번 글에서는 Three.js에서 OrbitControls를 사용했을 때 FPS처럼 키보드 WASD로 상하좌우로 카메라를 이동하는 방법에 대해 기록한다.


Three.js OrbitControls WASD 카메라 이동하는 방법

Three.js에서 WASD 카메라 이동하는 컨트롤의 경우 OrbitControls와 PointerLockControls가 있다. 이번 글에서는 OrbitControls를 사용하는 글이다.

 

1. OrbitControls를 정의한다.

controls = new OrbitControls( camera, renderer.domElement ); // scene 주위 공전
controls.enableDamping = true; // 부드러운 동작
controls.enablePan = false;
controls.enableZoom = false;
// controls.maxPolarAngle = Math.PI / 2; // 아래로 회전 제한 (바닥을 뚫지 않음)
controls.minPolarAngle = 0; // 위로 회전 제한

//controls.update()는 카메라 변환설정을 수동으로 변경한 후에 호출해야 합니다.
camera.position.set( 0, 20, 100 )

 

 

2. OrbitControls에 키입력 이벤트를 사용하여 카메라 이동을 할 수 있다.

// 키 입력 관리
const keys = {};

// 키 입력 이벤트
window.addEventListener('keydown', (event) => {
	keys[event.key] = true;
});

window.addEventListener('keyup', (event) => {
	keys[event.key] = false;
});

 

 

3. 각 키 이벤트에 대해 addScaledVector를 사용하여 카메라의 포지션을 이동시킨다.

function movePlayer() {
	const direction = new THREE.Vector3();
	const right = new THREE.Vector3();

	// 카메라 방향 기준으로 이동 벡터 계산
	camera.getWorldDirection(direction);
	direction.y = 0; // 수평 이동만 허용
	direction.normalize();

	right.crossVectors(camera.up, direction).normalize();

	if (keys['w']) {
		camera.position.addScaledVector(direction, moveSpeed); // 앞으로
	}
	if (keys['s']) {
		camera.position.addScaledVector(direction, -moveSpeed); // 뒤로
	}
	if (keys['a']) {
		camera.position.addScaledVector(right, moveSpeed); // 왼쪽으로
	}
	if (keys['d']) {
		camera.position.addScaledVector(right, -moveSpeed); // 오른쪽으로
	}
}

 

 

4. animate 중간에 movePlayer를 넣고, 그 다음으로 controls.update를 한다.

function animate() {
	requestAnimationFrame( animate );
	movePlayer();
	controls.update();
	renderer.render( scene, camera ); // sceene, camera 렌더링
}

 

 

5. 전체 소스 코드

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Exercise</title>
	<script type="importmap">
		{
            "imports": {
                        "three": "https://unpkg.com/three/build/three.module.js",
                        "three/addons/": "https://unpkg.com/three/examples/jsm/"
                        }
        }
	</script>

	<script type="module">
		import * as THREE from 'three';
		import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

		var scene, camera, renderer, controls;

		// 센터 바닥 default
		var base;
		var baseX = 0;
		var baseY = 0;
		var baseZ = 0;

		// 키 입력 관리
		const keys = {};

		// 키 입력 이벤트
		window.addEventListener('keydown', (event) => {
			keys[event.key] = true;
		});

		window.addEventListener('keyup', (event) => {
			keys[event.key] = false;
		});
		
		// 이동 속도
		const moveSpeed = 0.1;
		
		
		function movePlayer() {
			const direction = new THREE.Vector3();
			const right = new THREE.Vector3();

			// 카메라 방향 기준으로 이동 벡터 계산
			camera.getWorldDirection(direction);
			direction.y = 0; // 수평 이동만 허용
			direction.normalize();

			right.crossVectors(camera.up, direction).normalize();

			if (keys['w']) {
				camera.position.addScaledVector(direction, moveSpeed); // 앞으로
			}
			if (keys['s']) {
				camera.position.addScaledVector(direction, -moveSpeed); // 뒤로
			}
			if (keys['a']) {
				camera.position.addScaledVector(right, moveSpeed); // 왼쪽으로
			}
			if (keys['d']) {
				camera.position.addScaledVector(right, -moveSpeed); // 오른쪽으로
			}
		}

		function init() {
			// scene 생성
			scene = new THREE.Scene();
			// render 인스턴스 생성 및 크기 설정
			renderer = new THREE.WebGLRenderer();
			renderer.setSize( window.innerWidth, window.innerHeight );
			// 카메라 설정
			camera = new THREE.PerspectiveCamera( 45, 1, 1, 10000 ); // FOV, aspect, near, far

			camera.position.x = 80;
			camera.position.y = 50;
			camera.position.z = 70;

			//x-y-z axis (중심선)
			const axesHelper = new THREE.AxesHelper(3);
			axesHelper.position.x = 0;
			axesHelper.position.y = 0;
			axesHelper.position.z = 0;
			scene.add(axesHelper); // scene에 추가

			// grid helper
			const size = 100;
			const divisions = 100; // 나누는 개수
			const gridHelper = new THREE.GridHelper( size, divisions, 0x444444, 0x888888 );
			gridHelper.position.set(baseX/2,baseY/2,baseZ/2); // 그리드 중앙선 기준으로 (0, 0, 0)
			scene.add( gridHelper );


			document.body.appendChild( renderer.domElement ); // canvas 태그

			controls = new OrbitControls( camera, renderer.domElement ); // scene 주위 공전
			controls.enableDamping = true; // 부드러운 동작
			controls.enablePan = false;
			controls.enableZoom = false;
			// controls.maxPolarAngle = Math.PI / 2; // 아래로 회전 제한 (바닥을 뚫지 않음)
			controls.minPolarAngle = 0; // 위로 회전 제한

			//controls.update()는 카메라 변환설정을 수동으로 변경한 후에 호출해야 합니다.
			camera.position.set( 0, 20, 100 )
		}

		function animate() {
			requestAnimationFrame( animate );
			movePlayer();
			controls.update();

			renderer.render( scene, camera ); // sceene, camera 렌더링
		}

		init();
		animate();

	</script>


</head>
<body>
</body>
</html>

 

 

6. 결과


반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기