[Three.js] 物体をドラッグしてみる

物体をドラッグするのに、`DragControl` というのが使える。
ということが分かったのでやってみました。

この辺の記事を参考にしました。

https://sbcode.net/threejs/drag-controls/

https://akiyah.hatenablog.com/entry/2016/11/30/012400

https://qiita.com/naotaro0123/items/dc9c712233d2f64aaae3

https://www.youtube.com/watch?v=YyK6o8MhUf4


# この箱をドラッグして動かせるようにする。

![22.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/051c6c27-0c24-e519-8f1c-a54ba42f6faf.png)

以前の掲載した、この記事のコードをベースに、

https://qiita.com/sasuke_sss/items/c563a2ff2073dcdc22e0

(上記の記事の最初に掲載されているコードに対して、）
importsに、three/addons/ を追加する。
```html:html
    <script type="importmap">
      {
        "imports": {
          "three": "https://cdn.jsdelivr.net/npm/three@0.164.1/build/three.module.js",
          "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.164.1/examples/jsm/"
        }
      }
    </script>
```
DragControls を import する。
```html:html
    <script type="module">
      import * as THREE from "three";
      import { DragControls } from "three/addons/controls/DragControls.js";
```

「レンダラーを作成」の部分は、もともと以下のコードでしたが、
```javascript:html(変更前)
      // レンダラーを作成
      const renderer = new THREE.WebGLRenderer({
        canvas: document.querySelector("#myCanvas"),
      });
```

後でDragControlsの引数として使うので、変数canvasElementを作成してから設定するように変更。
```javascript:html(変更後)
      // レンダラーを作成
      const canvasElement = document.querySelector("#myCanvas");
      const renderer = new THREE.WebGLRenderer({
        canvas: canvasElement,
      });
```

なんとなく箱が大きすぎて見にくいと思ったので、もともと400,400,400でしたが、300,300,300に変更。
（ここは変更してもしなくてもよい）
```javascript:html
      // 箱を作成
      const geometry = new THREE.BoxGeometry(300, 300, 300);
```

「箱を作成」後に、「ドラッグコントローラーを作成」以下のコードを追加する。
```javascript:html
      // 箱を作成
      const geometry = new THREE.BoxGeometry(300, 300, 300);
      const material = new THREE.MeshNormalMaterial();
      const box = new THREE.Mesh(geometry, material);
      scene.add(box);

      // ドラッグコントローラーを作成

      let objects = [box]; //第1引数は配列じゃないとだめ。
      const controls_drag = new DragControls(objects, camera, canvasElement);
```

これで実行すると、
初めは箱は中心にあるのですが、

![23.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/254fa766-a25b-85a6-8a8b-a87b54b174af.png)

ドラッグして下とか、上とかに移動できるようになりました！

![24.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/774f6f20-ce73-ecec-1716-22ff02cc8d3e.png)

![25.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/6ca7d4c9-1d6c-063e-4c5b-4157aefe0c43.png)

### CodePen で書いた
<p class="codepen" data-height="700" data-default-tab="html,result" data-slug-hash="GgKqMMr" data-pen-title="three.js 入門 5_1" data-user="vhmbdiog-the-flexboxer" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/vhmbdiog-the-flexboxer/pen/GgKqMMr">
  three.js 入門 5_1</a> by sasuke (<a href="https://codepen.io/vhmbdiog-the-flexboxer">@vhmbdiog-the-flexboxer</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

# ハマった点
ここでハマったのが、<B>「DragControlsの第1引数は配列じゃないとだめ」</B>っていうこと。

今回はboxが1個だけですが、第1引数がboxだとエラーで動きません。
```javascript:html
      const controls_drag = new DragControls(box, camera, canvasElement);
```
こうする必要があります。
```javascript:html
      let objects = [box]; //第1引数は配列じゃないとだめ。
      const controls_drag = new DragControls(objects, camera, canvasElement);
```
複数の物体(object)を動かす場合は、それらを配列にして入れられるようになっているらしいです。

公式ドキュメントには、第1引数の定義は、
> objects: An array of draggable 3D objects.

と書いてあります。

https://threejs.org/docs/?q=drag#examples/en/controls/DragControls



# コード全体

```html:html
<html>
  <head>
    <meta charset="utf-8" />
    <script type="importmap">
      {
        "imports": {
          "three": "https://cdn.jsdelivr.net/npm/three@0.164.1/build/three.module.js",
          "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.164.1/examples/jsm/"
        }
      }
    </script>
    <script type="module">
      import * as THREE from "three";
      import { DragControls } from "three/addons/controls/DragControls.js";

      // サイズを指定
      const width = 960;
      const height = 540;

      // レンダラーを作成
      const canvasElement = document.querySelector("#myCanvas");
      const renderer = new THREE.WebGLRenderer({
        canvas: canvasElement,
      });
      renderer.setPixelRatio(window.devicePixelRatio);
      renderer.setSize(width, height);

      // シーンを作成
      const scene = new THREE.Scene();

      // カメラを作成
      const camera = new THREE.PerspectiveCamera(45, width / height);
      camera.position.set(0, 0, +1000);

      // 箱を作成
      const geometry = new THREE.BoxGeometry(300, 300, 300);
      const material = new THREE.MeshNormalMaterial();
      const box = new THREE.Mesh(geometry, material);
      scene.add(box);

      // ドラッグコントローラーを作成

      //第1引数は配列じゃないとだめ。
      let objects = [box]; //これがないとだめ
      const controls_drag = new DragControls(objects, camera, canvasElement);

      tick();

      // 毎フレーム時に実行されるループイベントです
      function tick() {
        box.rotation.y += 0.01;
        renderer.render(scene, camera); // レンダリング

        requestAnimationFrame(tick);
      }
    </script>
  </head>
  <body>
    <canvas id="myCanvas"></canvas>
  </body>
</html>
```

# 箱を複数個にしてみる。
「箱を作成」の部分を変更して、箱を複数にしてみる。

```javascript:html      
      // 箱1を作成
      const geometry = new THREE.BoxGeometry(300, 300, 300);
      const material = new THREE.MeshNormalMaterial();
      const box1 = new THREE.Mesh(geometry, material);
      scene.add(box1);

      // 箱2を作成
      const geometry2 = new THREE.BoxGeometry(200, 200, 200);
      const box2 = new THREE.Mesh(geometry2, material);
      //場所を変える
      box2.position.x = 400;
      box2.position.z = 400;
      scene.add(box2);

      // 箱3を作成
      const geometry3 = new THREE.BoxGeometry(100, 100, 100);
      const box3 = new THREE.Mesh(geometry2, material);
      //場所を変える
      box3.position.x = -600;
      box3.position.z = -400;
      scene.add(box3);
```

「ドラッグコントローラーを作成」の部分は、第1引数を配列 [box1, box2, box3] にして入れる。

```javascript:html      
      // ドラッグコントローラーを作成
      let objects = [box1, box2, box3]; //第1引数は配列じゃないとだめ。
      const controls_drag = new DragControls(objects, camera, canvasElement);
```

初期位置

![26.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/275d2841-6154-e100-7fe8-81cba156c5f0.png)

ドラッグで動かすと、こんな感じで動かせる。

![27.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/797228eb-2a4d-ee05-96d7-2bd3c4b45361.png)


![28.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/e54d1286-23b6-36c7-721e-2438925d5158.png)

### CodePen で書いた
<p class="codepen" data-height="700" data-default-tab="html,result" data-slug-hash="PwYzJrQ" data-pen-title="three.js 入門 5_2" data-user="vhmbdiog-the-flexboxer" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/vhmbdiog-the-flexboxer/pen/PwYzJrQ">
  three.js 入門 5_2</a> by sasuke (<a href="https://codepen.io/vhmbdiog-the-flexboxer">@vhmbdiog-the-flexboxer</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>