diff options
author | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:40:17 -0500 |
---|---|---|
committer | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:40:17 -0500 |
commit | 33058f2b292b3a581333bdfb21b8f671898c5060 (patch) | |
tree | 51c3e392513ec574331b2d3f85c394445ea803c6 /files/ja/games/techniques/3d_collision_detection | |
parent | 8b66d724f7caf0157093fb09cfec8fbd0c6ad50a (diff) | |
download | translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.gz translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.bz2 translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.zip |
initial commit
Diffstat (limited to 'files/ja/games/techniques/3d_collision_detection')
-rw-r--r-- | files/ja/games/techniques/3d_collision_detection/bounding_volume_collision_detection_with_three.js/index.html | 176 | ||||
-rw-r--r-- | files/ja/games/techniques/3d_collision_detection/index.html | 155 |
2 files changed, 331 insertions, 0 deletions
diff --git a/files/ja/games/techniques/3d_collision_detection/bounding_volume_collision_detection_with_three.js/index.html b/files/ja/games/techniques/3d_collision_detection/bounding_volume_collision_detection_with_three.js/index.html new file mode 100644 index 0000000000..3a6bea3106 --- /dev/null +++ b/files/ja/games/techniques/3d_collision_detection/bounding_volume_collision_detection_with_three.js/index.html @@ -0,0 +1,176 @@ +--- +title: THREE.js によるバウンディングボリューム衝突検出 +slug: >- + Games/Techniques/3D_collision_detection/Bounding_volume_collision_detection_with_THREE.js +tags: + - 3D + - Games + - JavaScript + - WebGL + - bounding boxes + - collision detection + - three.js +translation_of: >- + Games/Techniques/3D_collision_detection/Bounding_volume_collision_detection_with_THREE.js +--- +<div>{{GamesSidebar}}</div> + +<p class="summary">この記事では、<strong>Three.js ライブラリーを使用してバウンディングボックスとバウンディングスフィアの間の衝突検出</strong>を実装する方法を示します。 これを読む前に、まず <a href="/ja/docs/Games/Techniques/3D_collision_detection">3D 衝突検出</a>の紹介記事を読み、かつ Three.js に関する基本的な知識があることを前提としています。</p> + +<h2 id="Using_Box3_and_Sphere" name="Using_Box3_and_Sphere"><code>Box3</code> と <code>Sphere</code> の使用</h2> + +<p>Three.js には、<strong>数学的なボリューム</strong>(mathematical volumes)と形状を表すオブジェクトがあります。 3D の AABB とバウンディングスフィアには、<strong><code><a href="https://threejs.org/docs/#api/math/Box3">Box3</a></code></strong> オブジェクトと <strong><code><a href="https://threejs.org/docs/#api/math/Sphere">Sphere</a></code></strong> オブジェクトを使用できます。 インスタンス化されると、他のボリュームに対して交差テストを実行するために使用できるメソッドがあります。</p> + +<h3 id="Instantiating_boxes" name="Instantiating_boxes">ボックスのインスタンス化</h3> + +<p><strong><code>Box3</code> インスタンス</strong>を作成するには、ボックスの<strong>下と上の境界</strong>(対角線の2つの座標)を指定する必要があります。 通常、この AABB を 3D ワールドのオブジェクト(文字など)に「リンク」する必要があります。 Three.js では、<code>Geometry</code> インスタンスにオブジェクトの <code>min</code> と <code>max</code> の境界を持つ <code>boundingBox</code> プロパティがあります。 このプロパティを定義するには、事前に <code>Geometry.computeBoundingBox</code> を手動で呼び出す必要があることに注意してください。</p> + +<pre class="brush: js notranslate">var knot = new THREE.Mesh( + new THREE.TorusKnotGeometry(0.5, 0.1), + new MeshNormalMaterial({})); + +knot.geometry.computeBoundingBox(); +var knotBBox = new Box3( + knot.geometry.boundingBox.min, + knot.geometry.boundingBox.max); +</pre> + +<div class="note"> +<p><strong>注</strong>: <code>boundingBox</code> プロパティは、<code>Mesh</code> ではなく、<code>Geometry</code> 自体を参照として使用します。 したがって、<code>Mesh</code> に適用された拡大縮小、位置などの変換は、計算するボックスの計算では無視されます。</p> +</div> + +<p>前の問題を修正するより簡単な代替方法は、後で <code>Box3.setFromObject</code> を使用してこれらの境界を設定することです。 これにより、3D エンティティの<strong>変換<em>と</em>子メッシュ</strong>も考慮して寸法が計算されます。</p> + +<pre class="brush: js notranslate">var knot = new THREE.Mesh( + new THREE.TorusKnotGeometry(0.5, 0.1), + new MeshNormalMaterial({})); + +var knotBBox = new Box3(new THREE.Vector3(), new THREE.Vector3()); +knotBBox.setFromObject(knot);</pre> + +<h3 id="Instantiating_spheres" name="Instantiating_spheres">球のインスタンス化</h3> + +<p><strong><code>Sphere</code> オブジェクト</strong>のインスタンス化も同様です。 球の中心と半径を指定する必要があります。 これらは、<code>Geometry</code> で使用可能な <code>boundingSphere</code> プロパティで追加できます。</p> + +<pre class="brush: js notranslate">var knot = new THREE.Mesh( + new THREE.TorusKnotGeometry(0.5, 0.1), + new MeshNormalMaterial({})); + +var knotBSphere = new Sphere( + knot.position, + knot.geometry.boundingSphere.radius); +</pre> + +<p>残念ながら、<code>Sphere</code> インスタンスに <code>Box3.setFromObject</code> に相当するものはありません。 したがって、変換を適用したり、<code>Mesh</code> の位置を変更したりする場合は、バウンディングスフィアを手動で更新する必要があります。 例えば次のようにです。</p> + +<pre class="notranslate">knot.scale.set(2, 2, 2); +knotBSphere.radius = knot.geometry.radius * 2; +</pre> + +<h3 id="Intersection_tests" name="Intersection_tests">交差テスト</h3> + +<h4 id="Point_vs._Box3_Sphere" name="Point_vs._Box3_Sphere">点 対 <code>Box3</code> / <code>Sphere</code></h4> + +<p><code>Box3</code> と <code>Sphere</code> の両方に、このテストを実行するための <strong><code>containsPoint</code></strong> メソッドがあります。</p> + +<pre class="brush: js notranslate">var point = new THREE.Vector3(2, 4, 7); +knotBBox.containsPoint(point);</pre> + +<h4 id="Box3_vs._Box3" name="Box3_vs._Box3"><code>Box3</code> 対 <code>Box3</code></h4> + +<p><strong><code>Box3.intersectsBox</code></strong> メソッドは、このテストを実行するために使用できます。</p> + +<pre class="brush: js notranslate">knotBbox.intersectsBox(otherBox);</pre> + +<div class="note"> +<p><strong>注</strong>: これは、<code>Box3</code> が別のものを<em>完全に</em>包んでいるかどうかをチェックする <code>Box3.containsBox</code> メソッドとは異なります。</p> +</div> + +<h4 id="Sphere_vs._Sphere" name="Sphere_vs._Sphere"><code>Sphere</code> 対 <code>Sphere</code></h4> + +<p>前のものと同様の方法で、このテストを実行するための <strong><code>Sphere.intersectsSphere</code></strong> メソッドがあります。</p> + +<pre class="brush: js notranslate">knotBSphere.intersectsSphere(otherSphere);</pre> + +<h4 id="Sphere_vs._Box3" name="Sphere_vs._Box3"><code>Sphere</code> 対 <code>Box3</code></h4> + +<p>残念ながら、このテストは Three.js には実装されていませんが、<code>Sphere</code> にパッチを適用して<a href="/ja/docs/Games/Techniques/3D_collision_detection">球対 AABB の交差</a>アルゴリズムを実装できます。</p> + +<pre class="brush: js notranslate">// THREE.js の Sphere を展開して、対 Box3 衝突テストをサポートします。 +// チェックのたびに Vector3 の新しいインスタンスが生成されないように、 +// メソッドスコープ外でベクターを作成しています。 + +THREE.Sphere.__closest = new THREE.Vector3(); +THREE.Sphere.prototype.intersectsBox = function (box) { + // get box closest point to sphere center by clamping + THREE.Sphere.__closest.set(this.center.x, this.center.y, this.center.z); + THREE.Sphere.__closest.clamp(box.min, box.max); + + var distance = this.center.distanceToSquared(THREE.Sphere.__closest); + return distance < (this.radius * this.radius); +};</pre> + +<h3 id="Demos" name="Demos">デモ</h3> + +<p>これらの手法を示すために、いくつかの<a href="http://mozdevs.github.io/gamedev-js-3d-aabb/">ライブデモ</a>と、調べるための<a href="https://github.com/mozdevs/gamedev-js-3d-aabb">ソースコード</a>を用意しました。</p> + +<ul> + <li><a href="http://mozdevs.github.io/gamedev-js-3d-aabb/raw_point.html">点 対 ボックスと球</a></li> + <li><a href="http://mozdevs.github.io/gamedev-js-3d-aabb/raw_box.html">ボックス 対 ボックスと球</a></li> + <li><a href="http://mozdevs.github.io/gamedev-js-3d-aabb/raw_sphere.html">球 対 ボックスと球</a></li> +</ul> + +<p><img alt="" src="https://mdn.mozillademos.org/files/11825/Screen%20Shot%202015-10-20%20at%2015.19.16.png" style="display: block; height: 256px; margin: 0px auto; width: 256px;"></p> + +<h2 id="Using_BoxHelper" name="Using_BoxHelper"><code>BoxHelper</code> の使用</h2> + +<p>生の <code>Box3</code> オブジェクトと <code>Sphere</code> オブジェクトを使用する代わりに、Three.js には、<strong>バウンディングボックスの処理を容易にする</strong>便利なオブジェクト <strong><code><a href="https://threejs.org/docs/#api/helpers/BoxHelper">BoxHelper</a></code></strong> があります(以前の <code>BoundingBoxHelper</code> は非推奨となりました)。 このヘルパーは <code>Mesh</code> を取り、そのバウンディングボックスのボリューム(子メッシュを含む)を計算します。 これにより、バウンディングボックスを表す新しいボックスの <code>Mesh</code> が作成されます。 これは、バウンディングボックスの形状を示し、<code>Mesh</code> と一致するバウンディングボックスを作成するために、前に見た <code>setFromObject</code> メソッドに渡すことができます。</p> + +<div> +<p><code>BoxHelper</code> は、Three.js のバウンディングボリュームとの 3D 衝突を処理するための<strong>推奨される</strong>方法です。 球のテストをもらすことになりますが、このトレードオフにはそれだけの価値があります。</p> +</div> + +<p>このヘルパーを使用する利点は次のとおりです。</p> + +<ul> + <li>リンクした <code>Mesh</code> が回転したり、その寸法を変更した場合に、バウンディングボックスの <code>Mesh</code> の<strong>サイズを変更</strong>し、その<strong>位置</strong>を更新する <code>update()</code> メソッドがあります。</li> + <li>バウンディングボックスのサイズを計算するときに<strong>子メッシュが考慮される</strong>ため、元のメッシュとそのすべての子が包まれます。</li> + <li><code>BoxHelper</code> が作成する <code>Mesh</code> を<strong>レンダリングする</strong>ことで、衝突を簡単にデバッグできます。 デフォルトでは、<code>LineBasicMaterial</code> マテリアル(ワイヤーフレームスタイルのジオメトリーを描画するための three.js マテリアル)を使用して作成されます。</li> +</ul> + +<p>主な欠点は、<strong>ボックスのバウンディングボリュームのみを作成する</strong>ことです。 したがって、球対 AABB のテストが必要な場合は、独自の <code>Sphere</code> オブジェクトを作成する必要があります。</p> + +<p>これを使用するには、新しい <code>BoxHelper</code> インスタンスを作成し、ジオメトリーと、オプションで、ワイヤーフレームマテリアルに使用する色を指定する必要があります。 また、新しく作成したオブジェクトをレンダリングするには、three.js のシーンに追加する必要があります。 シーン変数は単に <code>scene</code> と呼ばれると仮定します。</p> + +<pre class="brush: js notranslate">var knot = new THREE.Mesh( + new THREE.TorusKnotGeometry(0.5, 0.1), + new THREE.MeshNormalMaterial({}) +); +var knotBoxHelper = new THREE.BoxHelper(knot, 0x00ff00); +scene.add(knotBoxHelper);</pre> + +<p>実際の <code>Box3</code> バウンディングボックスも作成するために、新しい <code>Box3</code> オブジェクトを作成し、<code>BoxHelper</code> の形状と位置を想定します。</p> + +<pre class="syntaxbox notranslate">var box3 = new THREE.Box3(); +box3.setFromObject(knotBoxHelper);</pre> + +<p><code>Mesh</code> の位置、回転、拡大縮小などを変更する場合は、<code>update()</code> メソッドを呼び出して、<code>BoxHelper</code> インスタンスがリンクした <code>Mesh</code> と一致するようにする必要があります。 <code>Box3</code> を <code>Mesh</code> に従わせるには、<code>setFromObject</code> を再度呼び出す必要もあります。</p> + +<pre class="syntaxbox notranslate">knot.position.set(-3, 2, 1); +knot.rotation.x = -Math.PI / 4; +// update the bounding box so it stills wraps the knot +knotBoxHelper.update(); +box3.setFromObject(knotBoxHelper);</pre> + +<p><strong>衝突テスト</strong>の実行は、上記のセクションで説明したのと同じ方法で実行します。 つまり、<code>Box3</code> オブジェクトを上記と同じ方法で使用します。</p> + +<pre class="brush: js notranslate">// box vs box +box3.intersectsBox(otherBox3); +// box vs point +box3.containsPoint(point.position);</pre> + +<h3 id="Demos_2" name="Demos_2">デモ</h3> + +<p><a href="http://mozdevs.github.io/gamedev-js-3d-aabb/">ライブデモのページ</a>で確認できる<strong>2つのデモ</strong>があります。 <a href="http://mozdevs.github.io/gamedev-js-3d-aabb/api_point.html">1つ目</a>は、<code>BoxHelper</code> を使用した点対ボックスの衝突を示しています。 <a href="http://mozdevs.github.io/gamedev-js-3d-aabb/api_box.html">2つ目</a>は、ボックス対ボックスのテストを実行します。</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/11821/Screen%20Shot%202015-10-19%20at%2012.10.06.png" style="display: block; height: 256px; margin: 0px auto; width: 256px;"></p> diff --git a/files/ja/games/techniques/3d_collision_detection/index.html b/files/ja/games/techniques/3d_collision_detection/index.html new file mode 100644 index 0000000000..d17e0d1acc --- /dev/null +++ b/files/ja/games/techniques/3d_collision_detection/index.html @@ -0,0 +1,155 @@ +--- +title: 3D 衝突検出 +slug: Games/Techniques/3D_collision_detection +tags: + - 3D + - Games + - JavaScript + - bounding boxes + - collision detection +translation_of: Games/Techniques/3D_collision_detection +--- +<div>{{GamesSidebar}}</div> + +<p class="summary">この記事では、3D 環境で衝突検出を実装するために使用されるさまざまなバウンディングボリューム手法の概要を説明します。 追求記事では、特定の 3D ライブラリでの実装について説明します。</p> + +<h2 id="Axis-aligned_bounding_boxes" name="Axis-aligned_bounding_boxes">座標軸に沿ったバウンディングボックス</h2> + +<p>2D 衝突検出と同様に、<strong>座標軸に沿ったバウンディングボックス</strong>(axis-aligned bounding boxes、AABB)は、2つのゲームエンティティがオーバーラップしているかどうかを判断するための最も速いアルゴリズムです。 これは、ゲームエンティティを回転しない(つまり座標軸に沿った)ボックスで包み、3D 座標空間でこれらのボックスの位置をチェックして、それらが重なっているかどうかを確認することで構成されます。</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/11797/Screen%20Shot%202015-10-16%20at%2015.11.21.png" style="display: block; height: 275px; margin: 0px auto; width: 432px;"></p> + +<p>パフォーマンス上の理由から、<strong>座標軸に沿った拘束</strong>(axis-aligned constraint)があります。 回転しない2つのボックス間の重複領域は、論理比較のみで確認できますが、回転するボックスには追加の三角関数操作が必要であり、計算に時間がかかります。 回転するエンティティがある場合は、バウンディングボックスの寸法を変更してオブジェクトを包むか、球(回転に対して不変)などの別のバウンディングジオメトリータイプを使用することを選択できます。 以下のアニメーション GIF は、回転するエンティティに合うようにサイズを調整する AABB の図形例を示しています。 ボックスは常に寸法を変更して、内部に含まれるエンティティにぴったりとフィットします。</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/11799/rotating_knot.gif" style="display: block; height: 192px; margin: 0px auto; width: 207px;"></p> + +<div class="note"> +<p><strong>注</strong>: この手法の実際の実装については、<a href="/ja/docs/Games/Techniques/3D_collision_detection/Bounding_volume_collision_detection_with_THREE.js">Three.js を使用したバウンディングボリューム</a>の記事を確認してください。</p> +</div> + +<h3 id="Point_vs._AABB" name="Point_vs._AABB">点 対 AABB</h3> + +<p>点が AABB 内にあるかどうかを確認するのは非常に簡単です。 点の座標が、AABB 内にあるかどうかを確認する必要があります。 各座標軸を個別に検討します。 <em>P<sub>x</sub></em>、<em>P<sub>y</sub></em>、<em>P<sub>z</sub></em> を点の座標、<em>B<sub>minX</sub></em>–<em>B<sub>maxX</sub></em>、<em>B<sub>minY</sub></em>–<em>B<sub>maxY</sub></em>、<em>B<sub>minZ</sub></em>–<em>B<sub>maxZ</sub></em> を AABB の各座標軸の範囲とすると、次の式を使用して、2つの間で衝突が発生したかどうかを計算できます。</p> + +<p><math><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>P</mi><mo>,</mo><mi>B</mi><mo stretchy="false">)</mo><mo>=</mo><mo stretchy="false">(</mo><msub><mi>P</mi><mi>x</mi></msub><mo>>=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi><mi>X</mi></mrow></msub><mo>∧</mo><msub><mi>P</mi><mi>x</mi></msub><mo><=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi><mi>X</mi></mrow></msub><mo stretchy="false">)</mo><mo>∧</mo><mo stretchy="false">(</mo><msub><mi>P</mi><mi>y</mi></msub><mo>>=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi><mi>Y</mi></mrow></msub><mo>∧</mo><msub><mi>P</mi><mi>y</mi></msub><mo><=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi><mi>Y</mi></mrow></msub><mo stretchy="false">)</mo><mo>∧</mo><mo stretchy="false">(</mo><msub><mi>P</mi><mi>z</mi></msub><mo>>=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi><mi>Z</mi></mrow></msub><mo>∧</mo><msub><mi>P</mi><mi>z</mi></msub><mo><=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi><mi>Z</mi></mrow></msub><mo stretchy="false">)</mo></mrow><annotation encoding="TeX">f(P,B)= (P_x >= B_{minX} \wedge P_x <= B_{maxX}) \wedge (P_y >= B_{minY} \wedge P_y <= B_{maxY}) \wedge (P_z >= B_{minZ} \wedge P_z <= B_{maxZ})</annotation></semantics></math></p> + +<p>あるいは、JavaScript では、次のようになります。</p> + +<pre class="brush: js notranslate">function isPointInsideAABB(point, box) { + return (point.x >= box.minX && point.x <= box.maxX) && + (point.y >= box.minY && point.y <= box.maxY) && + (point.z >= box.minZ && point.z <= box.maxZ); +}</pre> + +<h3 id="AABB_vs._AABB" name="AABB_vs._AABB">AABB 対 AABB</h3> + +<p>AABB が別の AABB と交差するかどうかのチェックは、点のテストと同様です。 ボックスの境界を使用して、座標軸ごとに1つのテストを実行する必要があります。 次の図は、X 軸上で実行するテストを示しています。 基本的には、<em>A<sub>minX</sub></em>–<em>A<sub>maxX</sub></em> と <em>B<sub>minX</sub></em>–<em>B<sub>maxX</sub></em> の範囲は重複しているかです。</p> + +<p><img alt="updated version" src="https://mdn.mozillademos.org/files/11813/aabb_test.png" style="display: block; height: 346px; margin: 0px auto; width: 461px;"></p> + +<p>数学的には、これは次のようになります。</p> + +<p><math><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>A</mi><mo>,</mo><mi>B</mi><mo stretchy="false">)</mo><mo>=</mo><mo stretchy="false">(</mo><msub><mi>A</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi><mi>X</mi></mrow></msub><mo><=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi><mi>X</mi></mrow></msub><mo>∧</mo><msub><mi>A</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi><mi>X</mi></mrow></msub><mo>>=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi><mi>X</mi></mrow></msub><mo stretchy="false">)</mo><mo>∧</mo><mo stretchy="false">(</mo><msub><mi>A</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi><mi>Y</mi></mrow></msub><mo><=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi><mi>Y</mi></mrow></msub><mo>∧</mo><msub><mi>A</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi><mi>Y</mi></mrow></msub><mo>>=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi><mi>Y</mi></mrow></msub><mo stretchy="false">)</mo><mo>∧</mo><mo stretchy="false">(</mo><msub><mi>A</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi><mi>Z</mi></mrow></msub><mo><=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi><mi>Z</mi></mrow></msub><mo>∧</mo><msub><mi>A</mi><mrow><mi>m</mi><mi>a</mi><mi>x</mi><mi>Z</mi></mrow></msub><mo>>=</mo><msub><mi>B</mi><mrow><mi>m</mi><mi>i</mi><mi>n</mi><mi>Z</mi></mrow></msub><mo stretchy="false">)</mo></mrow><annotation encoding="TeX">f(A,B) =</annotation></semantics></math></p> + +<p>そして、JavaScript では、これを使用します。</p> + +<pre class="brush: js notranslate">function intersect(a, b) { + return (a.minX <= b.maxX && a.maxX >= b.minX) && + (a.minY <= b.maxY && a.maxY >= b.minY) && + (a.minZ <= b.maxZ && a.maxZ >= b.minZ); +} +</pre> + +<h2 id="Bounding_spheres" name="Bounding_spheres">バウンディングスフィア</h2> + +<p>バウンディングスフィア(bounding spheres)を使用して衝突を検出することは、AABB よりも少し複雑ですが、それでもテストはかなり迅速です。 球の主な利点は、回転に対して不変であるため、包まれたエンティティが回転しても、バウンディングスフィアは同じままであるということです。 主な欠点は、包んでいるエンティティが実際に球形でない限り、包むのは通常適切ではないことです(つまり、バウンディングスフィアで人を包むと、多くの誤検知が発生しますので、AABB の方が適しています)。</p> + +<h3 id="Point_vs._sphere" name="Point_vs._sphere">点 対 球</h3> + +<p>球に点が含まれているかどうかを確認するには、点と球の中心との間の距離を計算する必要があります。 この距離が球の半径以下の場合、点は球の内側にあります。</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/11803/point_vs_sphere.png" style="display: block; height: 262px; margin: 0px auto; width: 385px;"></p> + +<p>2つの点 <em>A</em> と <em>B</em> の間のユークリッド距離が <math><semantics><msqrt><mrow><mo stretchy="false">(</mo><msub><mi>A</mi><mi>x</mi></msub><mo>-</mo><msub><mi>B</mi><mi>x</mi></msub><msup><mo stretchy="false">)</mo><mn>2</mn></msup><mo stretchy="false">)</mo><mo>+</mo><mo stretchy="false">(</mo><msub><mi>A</mi><mi>y</mi></msub><mo>-</mo><msub><mi>B</mi><mi>y</mi></msub><msup><mo stretchy="false">)</mo><mn>2</mn></msup><mo>+</mo><mo stretchy="false">(</mo><msub><mi>A</mi><mi>z</mi></msub><mo>-</mo><msub><mi>B</mi><mi>z</mi></msub><mo stretchy="false">)</mo></mrow></msqrt><annotation encoding="TeX">\sqrt{(A_x - B_x) ^ 2) + (A_y - B_y)^2 + (A_z - B_z)}</annotation></semantics></math> であることを考慮すると、点対球の衝突検出の式は次のようになります。</p> + +<p><math><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>P</mi><mo>,</mo><mi>S</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi>S</mi><mrow><mi>r</mi><mi>a</mi><mi>d</mi><mi>i</mi><mi>u</mi><mi>s</mi></mrow></msub><mo>>=</mo><msqrt><mrow><mo stretchy="false">(</mo><msub><mi>P</mi><mi>x</mi></msub><mo>-</mo><msub><mi>S</mi><mi>x</mi></msub><msup><mo stretchy="false">)</mo><mn>2</mn></msup><mo>+</mo><mo stretchy="false">(</mo><msub><mi>P</mi><mi>y</mi></msub><mo>-</mo><msub><mi>S</mi><mi>y</mi></msub><msup><mo stretchy="false">)</mo><mn>2</mn></msup><mo>+</mo><mo stretchy="false">(</mo><msub><mi>P</mi><mi>z</mi></msub><mo>-</mo><msub><mi>S</mi><mi>z</mi></msub><msup><mo stretchy="false">)</mo><mn>2</mn></msup></mrow></msqrt></mrow><annotation encoding="TeX">f(P,S) = S_{radius} >= \sqrt{(P_x - S_x)^2 + (P_y - S_y)^2 + (P_z - S_z)^2}</annotation></semantics></math></p> + +<p>あるいは、JavaScript では、次のようになります。</p> + +<pre class="brush: js notranslate">function isPointInsideSphere(point, sphere) { + // Math.pow を呼び出すよりも高速であるため、乗算を使用しています + var distance = Math.sqrt((point.x - sphere.x) * (point.x - sphere.x) + + (point.y - sphere.y) * (point.y - sphere.y) + + (point.z - sphere.z) * (point.z - sphere.z)); + return distance < sphere.radius; +} +</pre> + +<div class="note"> +<p>上記のコードは平方根を特徴としており、計算にコストがかかる可能性があります。 これを回避する簡単な最適化は、距離の2乗と半径の2乗を比較することで構成されているため、最適化された方程式には、代わりに <code>distanceSqr < sphere.radius * sphere.radius</code> が含まれます。</p> +</div> + +<h3 id="Sphere_vs._sphere" name="Sphere_vs._sphere">球 対 球</h3> + +<p>球対球のテストは、点対球のテストに似ています。 ここでテストする必要があるのは、球の中心間の距離がそれらの半径の合計以下であることです。</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/11805/sphere_vs_sphere.png" style="display: block; height: 262px; margin: 0px auto; width: 414px;"></p> + +<p>数学的には、これは次のようになります。</p> + +<p><math><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>A</mi><mo>,</mo><mi>B</mi><mo stretchy="false">)</mo><mo>=</mo><msqrt><mrow><mo stretchy="false">(</mo><msub><mi>A</mi><mi>x</mi></msub><mo>-</mo><msub><mi>B</mi><mi>x</mi></msub><msup><mo stretchy="false">)</mo><mn>2</mn></msup><mo>+</mo><mo stretchy="false">(</mo><msub><mi>A</mi><mi>y</mi></msub><mo>-</mo><msub><mi>B</mi><mi>y</mi></msub><msup><mo stretchy="false">)</mo><mn>2</mn></msup><mo>+</mo><mo stretchy="false">(</mo><msub><mi>A</mi><mi>z</mi></msub><mo>-</mo><msub><mi>B</mi><mi>z</mi></msub><msup><mo stretchy="false">)</mo><mn>2</mn></msup></mrow></msqrt><mo><=</mo><msub><mi>A</mi><mrow><mi>r</mi><mi>a</mi><mi>d</mi><mi>i</mi><mi>u</mi><mi>s</mi></mrow></msub><mo>+</mo><msub><mi>B</mi><mrow><mi>r</mi><mi>a</mi><mi>d</mi><mi>i</mi><mi>u</mi><mi>s</mi></mrow></msub></mrow><annotation encoding="TeX">f(A,B) = \sqrt{(A_x - B_x)^2 + (A_y - B_y)^2 + (A_z - B_z)^2} <= A_{radius} + B_{radius}</annotation></semantics></math></p> + +<p>あるいは、JavaScript では、次のようになります。</p> + +<pre class="brush: js notranslate">function intersect(sphere, other) { + // Math.pow を呼び出すよりも高速であるため、乗算を使用しています + var distance = Math.sqrt((sphere.x - other.x) * (sphere.x - other.x) + + (sphere.y - other.y) * (sphere.y - other.y) + + (sphere.z - other.z) * (sphere.z - other.z)); + return distance < (sphere.radius + other.radius); +}</pre> + +<h3 id="Sphere_vs._AABB" name="Sphere_vs._AABB">球 対 AABB</h3> + +<p>球と AABB が衝突しているかどうかのテストは少し複雑ですが、それでも単純で高速です。 論理的なアプローチは、AABB のすべての頂点をチェックし、それぞれに対して点対球のテストを実行することです。 ただし、これはやり過ぎです。 AABB の<em>最も近い点</em>(必ずしも頂点である必要はありません)と球の中心との間の距離を計算して、球の半径以下であるかどうかを確認するだけで済むため、すべての頂点をテストする必要はありません。 この値は、球の中心を AABB の限界にクランプすることで取得できます。</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/11837/sphere_vs_aabb.png" style="display: block; height: 282px; margin: 0px auto; width: 377px;"></p> + +<p>JavaScript では、次のようにこのテストを行います。</p> + +<pre class="syntaxbox notranslate">function intersect(sphere, box) { + // クランプして球の中心からボックスの最も近い点を取得します + var x = Math.max(box.minX, Math.min(sphere.x, box.maxX)); + var y = Math.max(box.minY, Math.min(sphere.y, box.maxY)); + var z = Math.max(box.minZ, Math.min(sphere.z, box.maxZ)); + + // これは isPointInsideSphere と同じです + var distance = Math.sqrt((x - sphere.x) * (x - sphere.x) + + (y - sphere.y) * (y - sphere.y) + + (z - sphere.z) * (z - sphere.z)); + + return distance < sphere.radius; +} +</pre> + +<h2 id="Using_a_physics_engine" name="Using_a_physics_engine">物理エンジンの使用</h2> + +<p><strong>3D 物理エンジン</strong>(3D physics engines)は、衝突検出アルゴリズムを提供していますが、そのほとんどは、バウンディングボリュームにも基づいています。 物理エンジンが機能する方法は、通常はその視覚的表現に付属した<strong>物理的なボディ</strong>(physical body)を作成することです。 このボディには、速度、位置、回転、トルクなどのプロパティと、<strong>物理的な形状</strong>(physical shape)があります。 この形状は、衝突検出の計算で考慮されるものです。</p> + +<p>このような手法が実際に動作していることを確認できる<a href="http://mozdevs.github.io/gamedev-js-3d-aabb/physics.html">ライブ衝突検出デモ</a>(<a href="https://github.com/mozdevs/gamedev-js-3d-aabb">ソースコード</a>付き)を用意しました。 これは、オープンソースの 3D 物理エンジン <a href="https://github.com/schteppe/cannon.js">cannon.js</a> を使用しています。</p> + +<h2 id="See_also" name="See_also">関連情報</h2> + +<p>MDN の関連記事</p> + +<ul> + <li><a href="/ja/docs/Games/Techniques/3D_collision_detection/Bounding_volume_collision_detection_with_THREE.js">Three.js によるバウンディングボリューム衝突検出</a></li> + <li><a href="/ja/docs/Games/Techniques/2D_collision_detection">2D 衝突検出</a></li> +</ul> + +<p>外部リソース</p> + +<ul> + <li>Gamasutra の<a href="http://www.gamasutra.com/view/feature/3383/simple_intersection_tests_for_games.php">ゲームのための簡単な交点テスト</a>(英語)</li> + <li>ウィキペディアの<a href="https://en.wikipedia.org/wiki/Bounding_volume">バウンディングボリューム</a>(英語)</li> +</ul> |