レイキャスターを利用した移動2
「レイキャスターを利用した移動1」では、1つの3Dオブジェクトを選択して移動する方法を紹介しました。しかし、実際にゲームやツールにこういったUIを使う場合は、複数あるオブジェクトから、任意の一つのオブジェクトを選択して移動したくなるでしょう。
「three.js」の「Raycaster」では、こういった処理に対応しています。前回説明しなかった、「intersectObject」関数の第2引数をtrueにすることで、複数の3Dオブジェクトを再帰的に検索して、光線が当たるすべての3Dオブジェクトを配列にして戻してくれます。
さて、この「複数の3Dオブジェクト」ですが、シーン中のすべてのオブジェクトではありません。指定したグループ配下のオブジェクトになります。そのため、まずはオブジェクトをグループ化する必要があります。
オブジェクトのグループは「Object3D」オブジェクトで行えます。「add」関数で3Dオブジェクトを追加してグループ化します。「intersectObject」関数の第1引数にこのグループを指定して、第2引数をtrueにすれば、光線が当たったオブジェクトの配列を得ることができます。
光線が当たったオブジェクトの参照は「intersects[ ].object」に格納されています。マウスの移動開始時点で、どのオブジェクトを選択したか記録しておけば、複数のオブジェクトが戻り値として戻ってきた場合も、操作対象を限定できます。
以下、レイキャスターを利用して、複数の3Dオブジェクトから、任意のオブジェクトを移動する「crocro.bc.useRaycaster2.js」を掲載します。
//= サンプル:レイキャスターを利用した移動
//== レイキャスターを利用した移動
crocro.bc.useRaycaster2 = function() {
// 変数の初期化
var d3 = crocro.bc.d3; // 3D格納用
var group = new THREE.Object3D(); // グループ
// 要素の作成と格納
plt = crocro.bc.mkColPlate(0xff0000);
plt.position.x = 50;
plt.position.y = 50;
plt.scale.x = 2;
plt.scale.y = 2;
plt.scale.z = 2;
group.add(plt);
plt = crocro.bc.mkColPlate(0x000ff0);
plt.position.x = -50;
plt.position.y = -50;
plt.position.z = 2;
plt.scale.x = 2;
plt.scale.y = 2;
plt.scale.z = 2;
group.add(plt);
d3.scene.add(group);
crocro.bc.render();
// マウス操作の作成
d3.tgtGrp = group;
crocro.bc.useRaycaster2_setMouse();
// 終了処理
crocro.bc.setFinalize(function() {
var d3 = crocro.bc.d3; // 3D格納用
d3.tgtGrp = null;
d3.tgtSel = null;
d3.prjctr = null;
})
};
//== マウス操作の作成
crocro.bc.useRaycaster2_setMouse = function() {
// 変数の初期化
var d3 = crocro.bc.d3; // 3D格納用
var cntnr = crocro.bc.cntnr; // コンテナー
var isAct = false;
var posX, posY;
var prjctr = new THREE.Projector(); // プロジェクター
var offset = new THREE.Vector3();
//=== イベントを元に光線の交差判定
var chckIntrsct = function(event) {
// 中心からの位置の取得
posX = event.pageX - cntnr.offset().left;
posY = event.pageY - cntnr.offset().top;
// 描画範囲の比率に変換
posX = (posX / d3.w) * 2 - 1;
posY = -(posY / d3.h) * 2 + 1;
// マウス座標の作成
var vector = new THREE.Vector3(posX, posY, 1);
// スクリーン座標系をオブジェクトの座標系に変換
prjctr.unprojectVector(vector, d3.camera);
// レイキャスターを作成
var ray = new THREE.Raycaster(
d3.camera.position, // 始点
vector.sub(d3.camera.position).normalize() // 向きベクトル
);
// 交差判定
var intersects = ray.intersectObject(d3.tgtGrp, true);
// 結果を戻す
return intersects;
};
//=== マウス操作 開始
var msStrt = function(event) {
isAct = true; // 動作状態に移行
// イベントを元に光線の交差判定
var intersects = chckIntrsct(event);
// 有効か確認
if (intersects.length > 0) {
// 操作対象を決定
d3.tgtSel = intersects[0].object;
// 交差したのでオフセットを記録
offset.copy(intersects[0].point).sub(d3.tgtSel.position);
} else {
isAct = false; // 動作状態を解除
}
};
//=== マウス操作 移動
var msMv = function(event) {
// 動作状態でなければ終了
if (! isAct) {return;}
// イベントを元に光線の交差判定
var intersects = chckIntrsct(event);
// 有効か確認
if (intersects.length > 0) {
// 開始時と同じ対象を選択
var tgtIntrsct = intersects[0];
for (var i = 0; i < intersects.length; i ++) {
if (intersects[i].object == d3.tgtSel) {
tgtIntrsct = intersects[i];
break;
}
}
// 交差しているので移動
var pos = tgtIntrsct.point.sub(offset);
d3.tgtSel.position = pos;
}
// レンダリング
crocro.bc.render();
};
//=== マウス操作 終了
var msEnd = function() {
isAct = false; // 動作状態を解除
};
//=== マウス操作の登録
cntnr.mousedown(msStrt);
cntnr.mousemove(msMv);
cntnr.mouseup(msEnd);
$(window).blur(msEnd);
}
