You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					357 lines
				
				18 KiB
			
		
		
			
		
	
	
					357 lines
				
				18 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								<!DOCTYPE html><html lang="ko"><head>
							 | 
						||
| 
								 | 
							
								    <meta charset="utf-8">
							 | 
						||
| 
								 | 
							
								    <title>VR - 3DOF Point to Select</title>
							 | 
						||
| 
								 | 
							
								    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
							 | 
						||
| 
								 | 
							
								    <meta name="twitter:card" content="summary_large_image">
							 | 
						||
| 
								 | 
							
								    <meta name="twitter:site" content="@threejs">
							 | 
						||
| 
								 | 
							
								    <meta name="twitter:title" content="Three.js – VR - 3DOF Point to Select">
							 | 
						||
| 
								 | 
							
								    <meta property="og:image" content="https://threejs.org/files/share.png">
							 | 
						||
| 
								 | 
							
								    <link rel="shortcut icon" href="/files/favicon_white.ico" media="(prefers-color-scheme: dark)">
							 | 
						||
| 
								 | 
							
								    <link rel="shortcut icon" href="/files/favicon.ico" media="(prefers-color-scheme: light)">
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    <link rel="stylesheet" href="/manual/resources/lesson.css">
							 | 
						||
| 
								 | 
							
								    <link rel="stylesheet" href="/manual/resources/lang.css">
							 | 
						||
| 
								 | 
							
								<!-- Import maps polyfill -->
							 | 
						||
| 
								 | 
							
								<!-- Remove this when import maps will be widely supported -->
							 | 
						||
| 
								 | 
							
								<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<script type="importmap">
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  "imports": {
							 | 
						||
| 
								 | 
							
								    "three": "../../build/three.module.js"
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								</script>
							 | 
						||
| 
								 | 
							
								    <link rel="stylesheet" href="/manual/ko/lang.css">
							 | 
						||
| 
								 | 
							
								  </head>
							 | 
						||
| 
								 | 
							
								  <body>
							 | 
						||
| 
								 | 
							
								    <div class="container">
							 | 
						||
| 
								 | 
							
								      <div class="lesson-title">
							 | 
						||
| 
								 | 
							
								        <h1>VR - 3DOF Point to Select</h1>
							 | 
						||
| 
								 | 
							
								      </div>
							 | 
						||
| 
								 | 
							
								      <div class="lesson">
							 | 
						||
| 
								 | 
							
								        <div class="lesson-main">
							 | 
						||
| 
								 | 
							
								          <p><strong>NOTE: 이 페이지의 예시에는 포인팅 장치가 있는 VR 지원 장치가 필요합니다.
							 | 
						||
| 
								 | 
							
								포인팅 장치가 있는 VR 지원 장치가 없으면 작업을 수행할 수 없으며
							 | 
						||
| 
								 | 
							
								그 이유를 <a href="webxr.html">이 글에서</a> 확인할 수 있습니다.
							 | 
						||
| 
								 | 
							
								</strong></p>
							 | 
						||
| 
								 | 
							
								<p><a href="webxr-look-to-select.html">이전 글</a>에서는 사용자가 보는 것을 통해 항목을 가리키며 선택할 수 있도록 하는 매우 간단한 VR 예제를 살펴보았습니다.
							 | 
						||
| 
								 | 
							
								이 글에서는 한 단계 더 나아가 사용자가 포인팅 장치를 사용하여 항목을 선택할 수 있도록 해보겠습니다.</p>
							 | 
						||
| 
								 | 
							
								<p>Three.js는 2개의 컨트롤러 개체를 VR로 제공하여 비교적 쉽게 만들며 단일 3DOF 컨트롤러와 2개의 6DOF 컨트롤러의 경우를 모두 처리하려고 합니다.
							 | 
						||
| 
								 | 
							
								각 컨트롤러는 컨트롤러의 방향과 위치를 제공하는 <a href="/docs/#api/ko/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> 개체입니다.
							 | 
						||
| 
								 | 
							
								또한 사용자가 컨트롤러의 "메인" 버튼을 누르기 시작하고, 누르고, 누르기를 중지할 때(끝낼 때) <code class="notranslate" translate="no">selectstart</code>, <code class="notranslate" translate="no">select</code> 및 <code class="notranslate" translate="no">selectend</code> 이벤트를 제공합니다.</p>
							 | 
						||
| 
								 | 
							
								<p><a href="webxr-look-to-select.html">이전 글</a>의 마지막 예시에서 <code class="notranslate" translate="no">PickHelper</code>를 <code class="notranslate" translate="no">ControllerPickHelper</code>로 변경해 보겠습니다.</p>
							 | 
						||
| 
								 | 
							
								<p>이번의 새로운 구현에서는 선택된 개체를 제공하는 <code class="notranslate" translate="no">select</code> 이벤트를 내보낼 것입니다.
							 | 
						||
| 
								 | 
							
								따라서 개체를 사용하기 위해 이 작업을 수행해야 합니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const pickHelper = new ControllerPickHelper(scene);
							 | 
						||
| 
								 | 
							
								pickHelper.addEventListener('select', (event) => {
							 | 
						||
| 
								 | 
							
								  event.selectedObject.visible = false;
							 | 
						||
| 
								 | 
							
								  const partnerObject = meshToMeshMap.get(event.selectedObject);
							 | 
						||
| 
								 | 
							
								  partnerObject.visible = true;
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>이전의 코드를 떠올려 보면 상자와 구를 서로 매핑하면 <code class="notranslate" translate="no">meshToMeshMap</code>를 통해 박스와 구를 찾을 수 있으므로
							 | 
						||
| 
								 | 
							
								여기서는 선택된 개체를 숨기고 파트너의 숨김을 해제합니다.</p>
							 | 
						||
| 
								 | 
							
								<p> <code class="notranslate" translate="no">ControllerPickHelper</code>의 실제 구현에 대해서는 먼저 VR 컨트롤러 개체를 scene에 추가하고
							 | 
						||
| 
								 | 
							
								 이러한 개체에 사용자가 가리키는 위치를 표시하는 데 사용할 수 있는 3D 라인을 추가하고, 컨트롤러와 라인을 모두 저장해야 합니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ControllerPickHelper {
							 | 
						||
| 
								 | 
							
								  constructor(scene) {
							 | 
						||
| 
								 | 
							
								    const pointerGeometry = new THREE.BufferGeometry().setFromPoints([
							 | 
						||
| 
								 | 
							
								      new THREE.Vector3(0, 0, 0),
							 | 
						||
| 
								 | 
							
								      new THREE.Vector3(0, 0, -1),
							 | 
						||
| 
								 | 
							
								    ]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this.controllers = [];
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < 2; ++i) {
							 | 
						||
| 
								 | 
							
								      const controller = renderer.xr.getController(i);
							 | 
						||
| 
								 | 
							
								      scene.add(controller);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      const line = new THREE.Line(pointerGeometry);
							 | 
						||
| 
								 | 
							
								      line.scale.z = 5;
							 | 
						||
| 
								 | 
							
								      controller.add(line);
							 | 
						||
| 
								 | 
							
								      this.controllers.push({controller, line});
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>다른 어떠한 작업을 수행하지 않고 이 작업만으로도
							 | 
						||
| 
								 | 
							
								사용자의 포인팅 장치가 어디에 있고 어느 쪽을 가리키고 있는지를 보여주는 scene에서 한 두개의 라인이 제공됩니다.</p>
							 | 
						||
| 
								 | 
							
								<p>다음에는 컨트롤러로 선택하는 코드를 추가해봅시다. 카메라가 아닌 것으로 선택하는 것은 이번이 처음입니다.
							 | 
						||
| 
								 | 
							
								<a href="picking.html">피킹에 관한 글</a>에서는 마우스나 손가락을 사용하여 선택하는 것이 카메라에서 화면으로 전달된다는 것을 의미했습니다.
							 | 
						||
| 
								 | 
							
								<a href="webxr-look-to-select.html">이전 글</a>에서는 카메라에 나오는 사용자가 어떤 식으로 다시 보이는지를 기준으로 선택했습니다.
							 | 
						||
| 
								 | 
							
								이번에는 카메라를 사용하지 않기 때문에 컨트롤러의 위치를 통해 선택합니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ControllerPickHelper {
							 | 
						||
| 
								 | 
							
								  constructor(scene) {
							 | 
						||
| 
								 | 
							
								+    this.raycaster = new THREE.Raycaster();
							 | 
						||
| 
								 | 
							
								+    this.objectToColorMap = new Map();
							 | 
						||
| 
								 | 
							
								+    this.controllerToObjectMap = new Map();
							 | 
						||
| 
								 | 
							
								+    this.tempMatrix = new THREE.Matrix4();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const pointerGeometry = new THREE.BufferGeometry().setFromPoints([
							 | 
						||
| 
								 | 
							
								      new THREE.Vector3(0, 0, 0),
							 | 
						||
| 
								 | 
							
								      new THREE.Vector3(0, 0, -1),
							 | 
						||
| 
								 | 
							
								    ]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this.controllers = [];
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < 2; ++i) {
							 | 
						||
| 
								 | 
							
								      const controller = renderer.xr.getController(i);
							 | 
						||
| 
								 | 
							
								      scene.add(controller);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      const line = new THREE.Line(pointerGeometry);
							 | 
						||
| 
								 | 
							
								      line.scale.z = 5;
							 | 
						||
| 
								 | 
							
								      controller.add(line);
							 | 
						||
| 
								 | 
							
								      this.controllers.push({controller, line});
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								+  update(scene, time) {
							 | 
						||
| 
								 | 
							
								+    this.reset();
							 | 
						||
| 
								 | 
							
								+    for (const {controller, line} of this.controllers) {
							 | 
						||
| 
								 | 
							
								+      // cast a ray through the from the controller
							 | 
						||
| 
								 | 
							
								+      this.tempMatrix.identity().extractRotation(controller.matrixWorld);
							 | 
						||
| 
								 | 
							
								+      this.raycaster.ray.origin.setFromMatrixPosition(controller.matrixWorld);
							 | 
						||
| 
								 | 
							
								+      this.raycaster.ray.direction.set(0, 0, -1).applyMatrix4(this.tempMatrix);
							 | 
						||
| 
								 | 
							
								+      // get the list of objects the ray intersected
							 | 
						||
| 
								 | 
							
								+      const intersections = this.raycaster.intersectObjects(scene.children);
							 | 
						||
| 
								 | 
							
								+      if (intersections.length) {
							 | 
						||
| 
								 | 
							
								+        const intersection = intersections[0];
							 | 
						||
| 
								 | 
							
								+        // make the line touch the object
							 | 
						||
| 
								 | 
							
								+        line.scale.z = intersection.distance;
							 | 
						||
| 
								 | 
							
								+        // pick the first object. It's the closest one
							 | 
						||
| 
								 | 
							
								+        const pickedObject = intersection.object;
							 | 
						||
| 
								 | 
							
								+        // save which object this controller picked
							 | 
						||
| 
								 | 
							
								+        this.controllerToObjectMap.set(controller, pickedObject);
							 | 
						||
| 
								 | 
							
								+        // highlight the object if we haven't already
							 | 
						||
| 
								 | 
							
								+        if (this.objectToColorMap.get(pickedObject) === undefined) {
							 | 
						||
| 
								 | 
							
								+          // save its color
							 | 
						||
| 
								 | 
							
								+          this.objectToColorMap.set(pickedObject, pickedObject.material.emissive.getHex());
							 | 
						||
| 
								 | 
							
								+          // set its emissive color to flashing red/yellow
							 | 
						||
| 
								 | 
							
								+          pickedObject.material.emissive.setHex((time * 8) % 2 > 1 ? 0xFF2000 : 0xFF0000);
							 | 
						||
| 
								 | 
							
								+        }
							 | 
						||
| 
								 | 
							
								+      } else {
							 | 
						||
| 
								 | 
							
								+        line.scale.z = 5;
							 | 
						||
| 
								 | 
							
								+      }
							 | 
						||
| 
								 | 
							
								+    }
							 | 
						||
| 
								 | 
							
								+  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p><a href="/docs/#api/ko/core/Raycaster"><code class="notranslate" translate="no">Raycaster</code></a>를 사용하기 전에는 그랬지만 이번에는 컨트롤러에서 ray를 가져옵니다.
							 | 
						||
| 
								 | 
							
								이전 <code class="notranslate" translate="no">PickHelper</code>에서는 한 가지만 선택할 수 있었지만, 여기서는 한 손에 하나씩 최대 2개의 컨트롤러가 있습니다.
							 | 
						||
| 
								 | 
							
								우리는 각 컨트롤러가 보고 있는 개체를 <code class="notranslate" translate="no">controllerToObjectMap</code>에 저장해야 합니다.
							 | 
						||
| 
								 | 
							
								또한 원래 방사체의 색을 <code class="notranslate" translate="no">objectToColorMap</code>에 저장하여 선이 가리키는 모든 부분에 닿을 수 있을 만큼 길게 만듭니다.</p>
							 | 
						||
| 
								 | 
							
								<p>모든 프레임에서 이러한 설정들을 재설정하려면 몇 가지 코드를 추가해야 합니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ControllerPickHelper {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								+  _reset() {
							 | 
						||
| 
								 | 
							
								+    // restore the colors
							 | 
						||
| 
								 | 
							
								+    this.objectToColorMap.forEach((color, object) => {
							 | 
						||
| 
								 | 
							
								+      object.material.emissive.setHex(color);
							 | 
						||
| 
								 | 
							
								+    });
							 | 
						||
| 
								 | 
							
								+    this.objectToColorMap.clear();
							 | 
						||
| 
								 | 
							
								+    this.controllerToObjectMap.clear();
							 | 
						||
| 
								 | 
							
								+  }
							 | 
						||
| 
								 | 
							
								  update(scene, time) {
							 | 
						||
| 
								 | 
							
								+    this._reset();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>다음으로 우리는 사용자가 컨트롤러를 클릭했을 때 <code class="notranslate" translate="no">select</code> 이벤트를 내보내야 합니다.
							 | 
						||
| 
								 | 
							
								이를위해 three.js의 <a href="/docs/#api/ko/core/EventDispatcher"><code class="notranslate" translate="no">EventDispatcher</code></a>를 확장한 후 컨트롤러에서 <code class="notranslate" translate="no">select</code> 이벤트를 확인하고
							 | 
						||
| 
								 | 
							
								해당 컨트롤러가 가리키는 것이 있으면 해당 컨트롤러가 가리키는 <code class="notranslate" translate="no">select</code> 이벤트를 내보냅니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-class ControllerPickHelper {
							 | 
						||
| 
								 | 
							
								+class ControllerPickHelper extends THREE.EventDispatcher {
							 | 
						||
| 
								 | 
							
								  constructor(scene) {
							 | 
						||
| 
								 | 
							
								+    super();
							 | 
						||
| 
								 | 
							
								    this.raycaster = new THREE.Raycaster();
							 | 
						||
| 
								 | 
							
								    this.objectToColorMap = new Map();  // object to save color and picked object
							 | 
						||
| 
								 | 
							
								    this.controllerToObjectMap = new Map();
							 | 
						||
| 
								 | 
							
								    this.tempMatrix = new THREE.Matrix4();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const pointerGeometry = new THREE.BufferGeometry().setFromPoints([
							 | 
						||
| 
								 | 
							
								      new THREE.Vector3(0, 0, 0),
							 | 
						||
| 
								 | 
							
								      new THREE.Vector3(0, 0, -1),
							 | 
						||
| 
								 | 
							
								    ]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this.controllers = [];
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < 2; ++i) {
							 | 
						||
| 
								 | 
							
								      const controller = renderer.xr.getController(i);
							 | 
						||
| 
								 | 
							
								+      controller.addEventListener('select', (event) => {
							 | 
						||
| 
								 | 
							
								+        const controller = event.target;
							 | 
						||
| 
								 | 
							
								+        const selectedObject = this.controllerToObjectMap.get(controller);
							 | 
						||
| 
								 | 
							
								+        if (selectedObject) {
							 | 
						||
| 
								 | 
							
								+          this.dispatchEvent({type: 'select', controller, selectedObject});
							 | 
						||
| 
								 | 
							
								+        }
							 | 
						||
| 
								 | 
							
								+      });
							 | 
						||
| 
								 | 
							
								      scene.add(controller);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      const line = new THREE.Line(pointerGeometry);
							 | 
						||
| 
								 | 
							
								      line.scale.z = 5;
							 | 
						||
| 
								 | 
							
								      controller.add(line);
							 | 
						||
| 
								 | 
							
								      this.controllers.push({controller, line});
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>이제 render loop에서 <code class="notranslate" translate="no">update</code>만 호출하면 됩니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								+  pickHelper.update(scene, time);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  renderer.render(scene, camera);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>그리고 컨트롤러가 있는 VR 장치가 있다고 가정하면 컨트롤러를 사용하여 선택할 수 있습니다.</p>
							 | 
						||
| 
								 | 
							
								<p></p><div translate="no" class="threejs_example_container notranslate">
							 | 
						||
| 
								 | 
							
								  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/webxr-point-to-select.html"></iframe></div>
							 | 
						||
| 
								 | 
							
								  <a class="threejs_center" href="/manual/examples/webxr-point-to-select.html" target="_blank">새 탭에서 보기</a>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p></p>
							 | 
						||
| 
								 | 
							
								<p>만약 우리가 물체를 움직일 수 있기를 원한다면 어떨까요?</p>
							 | 
						||
| 
								 | 
							
								<p>그것은 비교적 쉽습니다.
							 | 
						||
| 
								 | 
							
								컨트롤러의 <code class="notranslate" translate="no">select</code> 수신기 코드를 함수 안으로 이동하여 두가지 이상의 용도로 그것을 사용할 수 있도록 합니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ControllerPickHelper extends THREE.EventDispatcher {
							 | 
						||
| 
								 | 
							
								  constructor(scene) {
							 | 
						||
| 
								 | 
							
								    super();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this.controllers = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								+    const selectListener = (event) => {
							 | 
						||
| 
								 | 
							
								+      const controller = event.target;
							 | 
						||
| 
								 | 
							
								+      const selectedObject = this.controllerToObjectMap.get(event.target);
							 | 
						||
| 
								 | 
							
								+      if (selectedObject) {
							 | 
						||
| 
								 | 
							
								+        this.dispatchEvent({type: 'select', controller, selectedObject});
							 | 
						||
| 
								 | 
							
								+      }
							 | 
						||
| 
								 | 
							
								+    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < 2; ++i) {
							 | 
						||
| 
								 | 
							
								      const controller = renderer.xr.getController(i);
							 | 
						||
| 
								 | 
							
								-      controller.addEventListener('select', (event) => {
							 | 
						||
| 
								 | 
							
								-        const controller = event.target;
							 | 
						||
| 
								 | 
							
								-        const selectedObject = this.controllerToObjectMap.get(event.target);
							 | 
						||
| 
								 | 
							
								-        if (selectedObject) {
							 | 
						||
| 
								 | 
							
								-          this.dispatchEvent({type: 'select', controller, selectedObject});
							 | 
						||
| 
								 | 
							
								-        }
							 | 
						||
| 
								 | 
							
								-      });
							 | 
						||
| 
								 | 
							
								+      controller.addEventListener('select', selectListener);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								       ...
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>이제 이것을 <code class="notranslate" translate="no">selectstart</code>와 <code class="notranslate" translate="no">select</code> 모두에 사용해 봅시다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ControllerPickHelper extends THREE.EventDispatcher {
							 | 
						||
| 
								 | 
							
								  constructor(scene) {
							 | 
						||
| 
								 | 
							
								    super();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this.controllers = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const selectListener = (event) => {
							 | 
						||
| 
								 | 
							
								      const controller = event.target;
							 | 
						||
| 
								 | 
							
								      const selectedObject = this.controllerToObjectMap.get(event.target);
							 | 
						||
| 
								 | 
							
								      if (selectedObject) {
							 | 
						||
| 
								 | 
							
								-        this.dispatchEvent({type: 'select', controller, selectedObject});
							 | 
						||
| 
								 | 
							
								+        this.dispatchEvent({type: event.type, controller, selectedObject});
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < 2; ++i) {
							 | 
						||
| 
								 | 
							
								      const controller = renderer.xr.getController(i);
							 | 
						||
| 
								 | 
							
								      controller.addEventListener('select', selectListener);
							 | 
						||
| 
								 | 
							
								      controller.addEventListener('selectstart', selectListener);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								       ...
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>사용자가 컨트롤러의 버튼을 놓을 때 three.js가 전송하는 <code class="notranslate" translate="no">selectend</code> 이벤트도 전달해 봅시다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class ControllerPickHelper extends THREE.EventDispatcher {
							 | 
						||
| 
								 | 
							
								  constructor(scene) {
							 | 
						||
| 
								 | 
							
								    super();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    ...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this.controllers = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const selectListener = (event) => {
							 | 
						||
| 
								 | 
							
								      const controller = event.target;
							 | 
						||
| 
								 | 
							
								      const selectedObject = this.controllerToObjectMap.get(event.target);
							 | 
						||
| 
								 | 
							
								      if (selectedObject) {
							 | 
						||
| 
								 | 
							
								        this.dispatchEvent({type: event.type, controller, selectedObject});
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								+    const endListener = (event) => {
							 | 
						||
| 
								 | 
							
								+      const controller = event.target;
							 | 
						||
| 
								 | 
							
								+      this.dispatchEvent({type: event.type, controller});
							 | 
						||
| 
								 | 
							
								+    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < 2; ++i) {
							 | 
						||
| 
								 | 
							
								      const controller = renderer.xr.getController(i);
							 | 
						||
| 
								 | 
							
								      controller.addEventListener('select', selectListener);
							 | 
						||
| 
								 | 
							
								      controller.addEventListener('selectstart', selectListener);
							 | 
						||
| 
								 | 
							
								+      controller.addEventListener('selectend', endListener);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								       ...
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>이제 코드를 변경하여 <code class="notranslate" translate="no">selectstart</code> 이벤트가 발생하면 선택한 개체를 scene에서 제거하고 컨트롤러의 하위 개체로 만듭니다.
							 | 
						||
| 
								 | 
							
								즉, 컨트롤러와 함께 이동합니다. <code class="notranslate" translate="no">selectend</code>이벤트를 받게 되면 다시 scene에 넣을 것입니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const pickHelper = new ControllerPickHelper(scene);
							 | 
						||
| 
								 | 
							
								-pickHelper.addEventListener('select', (event) => {
							 | 
						||
| 
								 | 
							
								-  event.selectedObject.visible = false;
							 | 
						||
| 
								 | 
							
								-  const partnerObject = meshToMeshMap.get(event.selectedObject);
							 | 
						||
| 
								 | 
							
								-  partnerObject.visible = true;
							 | 
						||
| 
								 | 
							
								-});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								+const controllerToSelection = new Map();
							 | 
						||
| 
								 | 
							
								+pickHelper.addEventListener('selectstart', (event) => {
							 | 
						||
| 
								 | 
							
								+  const {controller, selectedObject} = event;
							 | 
						||
| 
								 | 
							
								+  const existingSelection = controllerToSelection.get(controller);
							 | 
						||
| 
								 | 
							
								+  if (!existingSelection) {
							 | 
						||
| 
								 | 
							
								+    controllerToSelection.set(controller, {
							 | 
						||
| 
								 | 
							
								+      object: selectedObject,
							 | 
						||
| 
								 | 
							
								+      parent: selectedObject.parent,
							 | 
						||
| 
								 | 
							
								+    });
							 | 
						||
| 
								 | 
							
								+    controller.attach(selectedObject);
							 | 
						||
| 
								 | 
							
								+  }
							 | 
						||
| 
								 | 
							
								+});
							 | 
						||
| 
								 | 
							
								+
							 | 
						||
| 
								 | 
							
								+pickHelper.addEventListener('selectend', (event) => {
							 | 
						||
| 
								 | 
							
								+  const {controller} = event;
							 | 
						||
| 
								 | 
							
								+  const selection = controllerToSelection.get(controller);
							 | 
						||
| 
								 | 
							
								+  if (selection) {
							 | 
						||
| 
								 | 
							
								+    controllerToSelection.delete(controller);
							 | 
						||
| 
								 | 
							
								+    selection.parent.attach(selection.object);
							 | 
						||
| 
								 | 
							
								+  }
							 | 
						||
| 
								 | 
							
								+});
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>한 개체가 선택되면 해당 개체와 원래의 부모 개체가 저장됩니다.
							 | 
						||
| 
								 | 
							
								사용자가 작업을 마치면 개체를 다시 돌려놓을 수 있습니다.</p>
							 | 
						||
| 
								 | 
							
								<p><a href="/docs/#api/ko/core/Object3D.attach"><code class="notranslate" translate="no">Object3D.attach</code></a>를 사용하여 선택한 개체를 재부모화 합니다.
							 | 
						||
| 
								 | 
							
								이러한 기능을 통해 scene에서 객체의 방향과 위치를 변경하지 않고도 객체의 부모를 변경할 수 있습니다.</p>
							 | 
						||
| 
								 | 
							
								<p>그리고 우리는 이를 통해 6DOF컨트롤러로 물체를 이동하거나 3DOF 컨트롤러로 방향을 전환할 수도 있을 것입니다.</p>
							 | 
						||
| 
								 | 
							
								<p></p><div translate="no" class="threejs_example_container notranslate">
							 | 
						||
| 
								 | 
							
								  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/webxr-point-to-select-w-move.html"></iframe></div>
							 | 
						||
| 
								 | 
							
								  <a class="threejs_center" href="/manual/examples/webxr-point-to-select-w-move.html" target="_blank">새 탭에서 보기</a>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p></p>
							 | 
						||
| 
								 | 
							
								<p>솔직하게 말해서 나는 이 <code class="notranslate" translate="no">ControllerPickHelper</code>가 코드를 구성하는 가장 좋은 방법이라고 확신할 수 없습니다.
							 | 
						||
| 
								 | 
							
								하지만 이것은 three.js의 VR에서 작동하는 간단한 작업들의 다양한 부분을 보여주는데 유용합니다.</p>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        </div>
							 | 
						||
| 
								 | 
							
								      </div>
							 | 
						||
| 
								 | 
							
								    </div>
							 | 
						||
| 
								 | 
							
								  
							 | 
						||
| 
								 | 
							
								  <script src="/manual/resources/prettify.js"></script>
							 | 
						||
| 
								 | 
							
								  <script src="/manual/resources/lesson.js"></script>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								</body></html>
							 |