카메라가 1인칭 시점으로 있을 때 마우스 움직임에 따라 화면을 회전시키는 방법에 대해 서술합니다.
1. 마우스 움직임 추적
마우스가 움직일 때를 다루는 이벤트는 'mousemove'이며, MouseEvent로 접근할 수 있습니다.
MouseEvent의 movementX, movementY 자체는 값이 많이 커서, 튀는 걸 방지하기 위해 작은 값을 곱해줍니다.
const mouseDel = new Vector2();
const updateMouse = (e: MouseEvent) => {
mouseDel.x = e.movementX * 0.001;
mouseDel.y = e.movementY * 0.001;
}
document.addEventListener('mousemove', updateMouse);
2. 카메라 회전
const euler = new Euler(0, 0, 0, 'YXZ');
const minPolarAngle = -Math.PI / 2;
const maxPolarAngle = Math.PI / 2;
const camOffset = new Vector3(0, 1, 0);
const rotate = (camera: Camera) => {
euler.setFromQuaternion(camera.quaternion);
// yaw
euler.y -= mouseDel.x;
// pitch
euler.x -= mouseDel.y;
// clamp
euler.x = Math.max(minPolarAngle, Math.min(maxPolarAngle, euler.x));
// smoothing
mouseDel.x *= 0.8;
mouseDel.y *= 0.8;
if (Math.abs(this.mouseDel.x) < 0.1) mouseDel.x = 0;
if (Math.abs(this.mouseDel.y) < 0.1) mouseDel.y = 0;
camera.quaternion.setFromEuler(euler);
// apply camer direction to the player direction
agent.rotation.y = euler.y;
// position
camera.position.copy(agent.position.clone().add(camOffset));
}
먼저 수정 사항을 적용할 euler 변수를 만들어 현재 카메라의 회전값을 설정해둡니다.
euler.setFromQuaternion(camera.quaternion);
updateMouse( )로 갱신한 마우스 이동값을 euler에 더해줍니다.
좌우 회전(mouseDel.x)은 y(수직축)축을 기준으로 회전하기 때문에 euler의 y축에 더해줍니다.
상하 회전(mouseDel.y)은 x(수평축)축을 기준으로 회전하기 때문에 euler의 x축에 더해줍니다.
단, 상하 회전의 경우 너무 위로, 또는 너무 아래로 돌아 넘어가지 않도록 상한선과 하한선을 정해줍니다.
// yaw
euler.y -= mouseDel.x;
// pitch
euler.x -= mouseDel.y;
// clamp
euler.x = Math.max(minPolarAngle, Math.min(maxPolarAngle, euler.x));
마우스 이동값을 조금씩 감쇠시켜 부드러운 움직임을 유도합니다.
단, 너무 작은 값이 되지 않게 일정 수치 이하가 되면 0으로 처리합니다.
// smoothing
mouseDel.x *= 0.8;
mouseDel.y *= 0.8;
if (Math.abs(this.mouseDel.x) < 0.1) mouseDel.x = 0;
if (Math.abs(this.mouseDel.y) < 0.1) mouseDel.y = 0;
이렇게 수정한 euler 값을 다시 카메라의 회전값에 집어 넣습니다.
또한 카메라가 따라다닐 플레이어(행위자)의 시점도 euler 값으로 설정합니다.
camera.quaternion.setFromEuler(euler);
// apply camera direction to the player direction
agent.rotation.y = euler.y;
마지막으로 카메라를 플레이어의 위치에 놓습니다.
플레이어 객체의 눈 높이를 반영하여 y=1로 하였습니다.
const camOffset = new Vector3(0, 1, 0);
// ...
// position
camera.position.copy(agent.position.clone().add(camOffset));
3. 적용
만든 rotate() 함수를 update 루프 함수 안에서 호출합니다.
useEffect(() => {
// ...
const update = () => {
// ...
controller.rotate(camera);
// ...
renderer.render(scene, camera);
renderer.setAnimationLoop(update);
}
}, []);728x90
'Frontend' 카테고리의 다른 글
| [Three] 10. Ray casting (0) | 2025.09.04 |
|---|---|
| [Three] Cannon.js - 물리엔진 적용하기 (1) | 2025.07.10 |
| [Three] 윈도우 크기에 따라 반응형으로 만들기 (0) | 2025.07.05 |
| [Three] 09. Loader - GLTF (0) | 2025.06.16 |
| [Three] 08. Material (0) | 2025.05.16 |