not good but great

プログラミング、アート、映画・本の感想について書きます。

Raycasterの当たり判定で壁をつくる

demo


W/A/S/Dまたは方向キーで移動、スペースキーでジャンプして階段を登ることが出来る。階段の床と壁の当たり判定を理解することに苦労した。

Rayを用いた3Dの当たり判定

Rayを用いて判定する。参考ページを見てもらうと理解しやすいと思う。
日記 | ヨモツネット

http://tmlife.net/programming/javascript/threejs-picking.html


ざっくり言うと(説明するのが難しいw)、二次元平面上の一点から奥へ光線(Ray)を引いて、Rayの延長上に物体があるかどうか判定する。

  //レイ
    ray = new THREE.Raycaster();
    //床方向
    ray.ray.direction.set(0,-1,0);

    raywall = new THREE.Raycaster();
    //壁の手前
    raywall.ray.direction.set(0,0,-1);
    //壁の奥
    // raywall.ray.direction.set(0,0,1);

Rayをどの方向に出すのか最初に決める。ここでは床と壁での当たり判定がしたいので2つ定義している。

参考デモとしてThree.jsをダウンロードしたフォルダにあるデモ「exmples/misc_controls_pointerlock.html」のコードをいじっている。最初、壁方向へのRayは設定してなかった。だから階段をつくったときに、床は機能していたが、少しでも壁に触れると貫通してしまい、うまく階段を登ることができなかった。

Rayの方向についてはデバッグしながら、XYZ方向を確認して記述するのが良いと思う。

障害物の定義

//メッシュの配列に入れる
objects.push(mesh);

対象となる障害物は配列に予め入れておく。生成したメッシュを配列にどんどんいれる。

var intersections = ray.intersectObjects(objects);

メッシュの配列をintersectObjectsに渡す。

documentの説明

intersectObjects
checks all intersection between the ray and the objects with or without the descendants.

intersectObjectの戻り値のプロパティ
http://www.atmarkit.co.jp/fwcr/design/benkyo/webgraphics05/05.html

distanceのプロパティについての説明は次のようになっている。

配列はdistanceが小さい順でソートされているので、配列が空でなければ、最初の要素がマウス座標の直下の交点となります。

 var distance = intersections[0].distance;

小さい順にソートされている関係で(console.logで調べるとゼロ番目以降はなかった?なぞだ)、配列の最初の要素のdistanceを見ればよい。その距離によって当たり判定の処理を決めていけば良い。

床の作り方

if(intersections.length > 0){//床と階段床への当たり判定
        var distance = intersections[0].distance;

        if(distance > 0 && distance < 10){
            controls.isOnObject(true);
        }
}

distanceの値がゼロちょうどでない理由は、幅をもたせないと判定がうまくいかないから。幅を小さくするとゆっくり衝突したときだけ、床になる。

当たり判定後はisOnObjectにtrueを渡している。

PointerLockControls.jsのソースを読む必要あり。
three.js/PointerLockControls.js at master · mrdoob/three.js · GitHub

Onとついているようにこれは始めから、床の当たり判定に用意された便利なメソッド。y軸方向の位置が維持されるように書かれている。詳しく言うとy軸方向への速度がゼロになる。

壁の作り方

かなり悩んだ。調べてもどこにも載っていない。

Rayを壁用にもう一つ設定しなければならないということに気づくのに時間がかかった。これを読んでヒントを得た。
javascript - camera and terrain collision on three.js - Stack Overflow

残念ながら、「isWallCollision()」という関数はない。以前は合ったのか始めからなかったのかはわからない。

・参考ページ
ボックスを動かして壁への当たり判定をしている。今回のデモとの違いは視点ではなくCubeだと言うこと。
Collision Detection (Three.js)

javascript - How to detect collision in three.js? - Stack Overflow


壁の衝突判定は複雑になると難しい。だから物理演算ライブラリを使うことも考えたが、学習コストがかかるので保留。
Physijs

http://chandlerprall.github.io/Physijs/

cannonjs

http://schteppe.github.io/cannon.js/

var wall_intersections = raywall.intersectObjects(objects);

if(wall_intersections.length > 0){//水平方向の壁の当たり判定
        var distance = wall_intersections[0].distance;

        if(distance > 0 && distance < 10){
            //視点を壁の手前で止める
            controls.getObject().position.z = wall_intersections[0].point.z + 20;
        }
}

ポイントは当たり判定の時に、pointを使ったこと。pointは交点の座標(THREE.Vector3)を保持している。だからz方向で壁の位置を取得して、それを視点のz座標に代入した。20足しているのは、これでうまくいったから。足さない、もしくは5とか10にするとカクカクとした動きになる。

マウスポインタを消す

マウスを視点移動に使うためにマウスポインタを消している。これは結構めんどくさいので、あんまり勉強せずに流した。

Pointer Lock and First Person Shooter Controls - HTML5 Rocks