카테고리 없음

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>

 

반응형