카테고리 없음
ThreeJS | PointerLockControls을 이용한 카메라 컨트롤러 만들기
이재원
2024. 6. 4. 13:19
PointerLockControls을 이용한 카메라 컨트롤러 만들기
js
class App {
constructor() {
this._models = [];
this._modelColorOptions = {};
this._data;
this._setupThreeJs()
this._setupCamera();
this._setupLight();
this._setupModel();
this._setupControls();
this._setupEvents();
this._pointer = new THREE.Vector2();
this._raycaster = new THREE.Raycaster();
//카메라이동 조작부관련
this._moveLeft = false;
this._moveRight = false;
this._moveFront = false;
this._moveBack = false;
this._rotateLeft = false;
this._rotateRight = false;
// 카메라 회전 각도 설정
this._rotationSpeed = 0.01;
// 직진 이동 거리 설정
this._moveSpeed = 0.08;
this._animate = this._animate.bind(this); // 컨텍스트 바인딩
this._setupInteractionEvents(); // 인터랙션 이벤트 설정 호출
// 이벤트 디스패처 생성
this.eventDispatcher = new THREE.EventDispatcher();
// 오브젝트 이벤트 처리 메서드 호출
this.currentObject = null;
this.handleObjectEvents();
// 애니메이션 시작
this._animate();
// 1분마다 업데이트
this._intervalId = setInterval(() => {
this.update();
}, 60000);
}
_setupEvents(){
window.onresize = this.resize.bind(this);
this.resize()
requestAnimationFrame(this.render.bind(this));
}
_onPointerMove(event) {
const rect = this._divContainer.getBoundingClientRect();
this._pointer.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
this._pointer.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
}
_setupThreeJs(){
const divContainer = document.querySelector("#webgl-container");
this._divContainer = divContainer;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
divContainer.appendChild(renderer.domElement);
this._renderer = renderer;
const scene = new THREE.Scene();
this._scene = scene;
}
_setupInteractionEvents() {
// 이벤트 리스너 설정
document.getElementById('left_rot').addEventListener('mousedown', () => {
this._rotateLeft = true;
});
document.getElementById('right_rot').addEventListener('mousedown', () => {
this._rotateRight = true;
});
document.getElementById('home_btn').addEventListener('mousedown', () => {
this._camera.position.y = 1.4;
this._camera.position.x = -3.5;
this._camera.position.z = 8;
this._camera.rotation.x = 0
this._camera.rotation.y =0
this._camera.rotation.z =0
});
document.getElementById('left_str').addEventListener('mousedown', () => {
this._moveLeft = true;
});
document.getElementById('right_str').addEventListener('mousedown', () => {
this._moveRight = true;
});
// 마우스 떼면 상태 초기화
window.addEventListener('mouseup', () => {
this._rotateLeft = false;
this._rotateRight = false;
this._moveLeft = false;
this._moveRight = false;
});
// 마우스 클릭 이벤트 리스너 등록
window.addEventListener('click', this.onClick.bind(this), false);
// 이벤트 리스너 설정
window.addEventListener('pointermove', this._onPointerMove.bind(this));
}
// 마우스 클릭 이벤트 핸들러
onClick = async (event) => {
const rect = this._divContainer.getBoundingClientRect();
// 마우스의 위치를 정규화된 장치 좌표로 변환
this._pointer.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
this._pointer.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
}
}
// 카메라 초기화
_setupCamera() {
const width = this._divContainer.clientWidth;
const height = this._divContainer.clientHeight;
const camera = new THREE.PerspectiveCamera(70, width / height, 0.1, 100);
camera.position.y = 1.4;
camera.position.x = -3.5;
camera.position.z = 8;
this._camera = camera;
document.addEventListener( 'mousewheel', (event) => {
this._camera.position.z +=event.deltaY/500;
});
}
//시점 위아래 고정
_setupControls() {
const controls = new PointerLockControls(this._camera, this._divContainer);
this._controls = controls;
controls.maxPolarAngle = Math.PI/2;
controls.minPolarAngle = Math.PI/2;
}
_animate(){
requestAnimationFrame(this._animate.bind(this));
// 회전 상태 확인
if(this._rotateLeft) {
this._camera.rotation.y += this._rotationSpeed;
}
if(this._rotateRight) {
this._camera.rotation.y -= this._rotationSpeed;
}
// 회전한상태에서도 옆으로 이동시 원래 옆방향으로 이동
if(this._moveLeft) {
const moveDirection = new THREE.Vector3(-1, 0, 0);
moveDirection.applyAxisAngle(new THREE.Vector3(0, 1, 0), this._camera.rotation.y);
moveDirection.z = 0; // Z축 이동 제거
moveDirection.multiplyScalar(this._moveSpeed);
this._camera.position.add(moveDirection);
}
if(this._moveRight) {
const moveDirection = new THREE.Vector3(1, 0, 0);
moveDirection.applyAxisAngle(new THREE.Vector3(0, 1, 0), this._camera.rotation.y);
moveDirection.z = 0; // Z축 이동 제거
moveDirection.multiplyScalar(this._moveSpeed);
this._camera.position.add(moveDirection);
}
}
render(time) {
// 카메라의 현재 회전값
const currentRotation = this._camera.rotation.clone();
if(currentRotation.y >= Math.PI / 5){
// y 축 주위의 회전값을 1.052로 설정
currentRotation.y = Math.PI / 5;
currentRotation.x = 0;
currentRotation.z = 0;
// 설정한 회전값을 카메라에 적용
this._camera.rotation.copy(currentRotation);
}else if(currentRotation.y <= - Math.PI / 5){
// y 축 주위의 회전값을 1.052로 설정
currentRotation.y = - Math.PI / 5;
currentRotation.x = 0;
currentRotation.z = 0;
// 설정한 회전값을 카메라에 적용
this._camera.rotation.copy(currentRotation);
}
// 카메라의 현재 위치.
const currentPosition = this._camera.position.clone();
if(currentPosition.x >= 3 ){
currentPosition.x = 3;
this._camera.position.copy(currentPosition);
}else if(currentPosition.x <= -10 ){
currentPosition.x = -10;
this._camera.position.copy(currentPosition);
}else if(currentPosition.z >= 10 ){
currentPosition.z = 10;
this._camera.position.copy(currentPosition);
}else if(currentPosition.z <= 4 ){
currentPosition.z = 4;
this._camera.position.copy(currentPosition);
}
this._renderer.render(this._scene, this._camera);
requestAnimationFrame(this.render.bind(this));
}
}
window.onload = function () {
new App();
};
html
<div id="content_second">
<div id="secondfloor" class ="secondfloor">
<div class="uppercontoller">
<button id="left_rot" class="ctrbtn"><img src="../js/egovframework/SEWER/dt/arrowicon/leftrotate.png" class="ico_rot" alt=""></button>
<button id="left_str" class="ctrbtn"><img src="../js/egovframework/SEWER/dt/arrowicon/left.png" class="ico_str" alt=""></button>
<button id="home_btn" class="ctrbtn"><img src="../js/egovframework/SEWER/dt/arrowicon/home.png" class="ico_str" alt=""></button>
<button id="right_str" class="ctrbtn"><img src="../js/egovframework/SEWER/dt/arrowicon/right.png" class="ico_str" alt=""></button>
<button id="right_rot" class="ctrbtn"><img src="../js/egovframework/SEWER/dt/arrowicon/rightrotate.png" class="ico_rot" alt=""></button>
</div>
</div>
<div id="webgl-container"></div>
반응형