3D物理エンジンと3D描画ライブラリでドラッグ&ドロップをするやり方
3D物理エンジンでテンセグリティのシミュレーションをしようと思って、Cannon.jsとかOimo.jsを触ってみているところです。ちょっと触ったCannon.jsではテンセグリティの張力(バネ)の使い方がややこしかったので、いまはOimo.jsで実験しています。
3D物理エンジンでのテンセグリティのシミュレーションは、棒の長さとか、張力の強さとかを簡単に変更して試したいという動機なんですが、できれば触っている感触を得たいじゃないですか。というわけでマウスでのドラッグ&ドロップをやってみようと思いました。
3次元の物体を2次元の画面上のマウスポインターでドラッグ&ドロップする方法を探して、Cannon.jsにちょうどいいサンプルがありましたのでそれを参考にします。考え方の話なので特定の3D物理エンジンと3D描画ライブラリには依存しませんが、ここではOimo.jsとthree.jsを想定して書きますね。あと、3D物理エンジンと3D描画ライブラリの両方をうまく組み合わせてドラッグ&ドロップを実現するので、なかなかややこしいです。一言で言えば、3D空間内にドラッグ用の平面を作って、そこでスプリングを使ってドラッグ&ドロップする、ですね。
↑ドラッグした直後にオブジェクトがずれることがあるので、なにか計算ミスしている気がします。。。
【登場オブジェクト】
3D物理エンジン側
- ドラッグ&ドロップされる対象のオブジェクト
- ドラッグ用の点
- ドラッグ用のスプリング
3D描画ライブラリ側
- カメラ
- ドラッグ用の平面
その他
- マウス
【ドラッグ&ドロップの流れ】
(1) マウスのボタンを押す
(1-1) カメラから見たマウスのポインターを3D空間上の直線に変換する
three.jsだとこんな感じです。
var ray = new THREE.Raycaster();
ray.setFromCamera(mouse, camera);
(1-2) その直線と「ドラッグ&ドロップされる対象のオブジェクト」の一番手前で交差する点を見つけて、ドラッグオブジェクトとドラッグポイントを使えるようにする。
three.jsだとこんな感じです。
var intersects = ray.intersectObjects(views, true);
if (0 < intersects.length) {
dragPoint = intersects[0].point;
dragBlockName = intersects[0].object.name;....
}
(1-3) そのドラッグオブジェクトとドラッグポイントを使って、「ドラッグ用の点」「ドラッグ用のスプリング」「ドラッグ用の平面」を配置する。
「ドラッグ用の点」は、ドラッグポイントに置きます。
「ドラッグ用のスプリング」は、「ドラッグ用の点」とドラッグオブジェクトのドラッグポイントを結ぶようにします。最初は始点と終点は同一の点です。
「ドラッグ用の平面」は、ドラッグポイントを中心としてカメラの視線に対して直行するように置きます。「ドラッグ用の平面」は画面上に表示する必要は無いのですが、開発中は見えているとわかりやすいので表示しておいたりします。下の画像のようになります(この画像ではドラッグ後にマウスがすこし動いたあとです)。
(2) マウスを移動する
(2-1) カメラから見たマウスのポインターを3D空間上の直線に変換する
これは(1-1)と同じです。
(2-2) その直線と「ドラッグ用の平面」の一番手前で交差する点を見つけて、ドラッグポイントを使えるようにする。
これは(1-2)と似ていますが、対象が「ドラッグ用の平面」になっています。
(2-3) そのドラッグポイントを使って、「ドラッグ用の点」「ドラッグ用の平面」を移動する。
「ドラッグ用の平面」を使って2次元の範囲で移動させるのがコツです。「ドラッグ用のスプリング」は始点と終点が離れていきます。それによって、ドラッグポイント(マウスの位置)にドラッグオブジェクトが引っ張られます。
(3) マウスのボタンを離す
(3-1) 「ドラッグ用の点」「ドラッグ用のスプリング」「ドラッグ用の平面」を消す。
これで一回のドラッグ&ドロップは終わりです。マウスのボタンを離す以外にも、マウスポインターがウィンドウから出た場合もドロップしたとみなしても良いかもしれません。
どうでしょうか。結構ややこしいですね。ドラッグ用のスプリングは3D物理エンジンの世界にあって、カメラやドラッグ用の平面は3D描画ライブラリの世界にあるので、どちらかのライブラリにプラグインを入れて解決、というわけにも行かないのが辛いところです。
私は3D物理エンジンに詳しいというわけではないので、もっと簡単にできるいいドラッグ&ドロップがあるのかもしれません。
Cannon.jsのサンプルに比べて、私が作ったものはキビキビしていないのがちょっと悔しいところです。改善予定です。そしてドラッグ&ドロップを使って3D空間内でテンセグリティをシミュレーションができるようにしたいです。