床にバウンドして火花が散る演出や、壁に跳ね返る弾丸エフェクトなど、ゲーム内の地形やオブジェクトと連動する物理表現は、ビジュアルの説得力を大きく向上させます。VFX Graphにはこれを実現するために「Collide with Plane」や「Collide with AABB」などの非常に優秀な衝突判定(コライダー)ノードが用意されています。しかし、「衝突設定を正しく組んだはずなのに、スピードの速いパーティクルや一部の粒子が床をすり抜けて下に沈んでいってしまう」という不具合に悩まされたことはないでしょうか。本記事では、このGPUパーティクル特有のコライダー突き抜け(トンネリング)現象が発生する技術的なメカニズムと、初心者にも分かりやすい例え話、そして完全にすり抜けを防ぐためのパラメータチューニング手法を徹底解説します。
1. 例え話で理解する:『薄いベニヤ板』と『瞬間移動するテニスボール』
この高速パーティクルが衝突判定を突き抜ける現象を理解するために、「非常に薄いベニヤ板(床)」に向かって「超高速でボール(パーティクル)を投げる」場面を例えに考えてみましょう。
ボールが秒速100メートルというもの凄いスピードで飛んでいます。ゲームの衝突判定システムは、現実世界のように「常にボールの軌道を追いかけている」わけではありません。システムは、「1フレーム目にボールがどこにあるか」「2フレーム目にボールがどこにあるか」という「静止画の点」の位置だけを調べています。
ボールの速度が速すぎるため、1フレーム目には「床の手前1メートル(空中)」にボールの点が映り、次の2フレーム目には「床を通り越した奥2メートル(地下)」にボールの点が映りました。
衝突判定の計算機(GPU)は、この2つの点だけを比較して「1フレーム目は手前、2フレーム目は奥。うん、床の板と『重なっている瞬間』はないね。だから衝突は起きていない!」と判断してしまいます。結果として、ボールは板をすり抜けて地下へ落ちていってしまいます。これが「トンネリング(突き抜け)現象」です。これを防ぐためには、ベニヤ板の裏側に「極厚のウレタンスポンジ(コライダーの厚み/Thickness)」を敷き詰め、ボールの点がどれだけ瞬間移動しても、その厚みの中に必ず引っかかるように対策(トラップ)を張る必要があります。
2. 根本的な原因:離散的衝突判定の限界とGPU更新の限界
技術的に言うと、VFX Graphの衝突判定ノードは、演算コストを抑えるために 離散的衝突判定(Discrete Collision Detection) を採用しています。これは毎フレームの「現在の位置」が、衝突面の内側にあるか外側にあるかだけを数式で評価する手法です。
以下の条件が重なると、衝突計算の境界を飛び越えてしまう確率が跳ね上がります。
- パーティクルの移動速度(Velocity)が極めて速い: $Velocity imes DeltaTime$ で計算される1フレームの移動量が、コライダーの判定範囲(球体の半径や平面の厚み)を上回る場合。
- 衝突対象の厚みがゼロである: `Collide with Plane` などの平面コライダーは、数学的に「厚みが無限に薄い(ゼロ)」であるため、一瞬のフレームで容易に通り抜けられます。
- GPUデプスバッファの解像度不足: `Collide with World (Depth)` を使用する場合、カメラの描画する深度テクスチャの解像度(特にニア/ファークリップの距離)が原因で、1ピクセルあたりの深度精度が粗くなり、衝突面が判定から漏れてすり抜ける現象が発生します。
3. 実務で採用すべき3つの解決アプローチ
エフェクトのスピードや要件に合わせて、以下の設定値を最適化します。
解決策A:Thickness (判定の厚み) と Radius (粒子半径) の最適化(最も基本)
衝突判定に使用しているノード(例:Collide with Plane または Collide with Sphere)の設定値をインスペクターで調整します。
- Thickness (コライダーの厚み): デフォルトの
0から `0.5` 〜 `1.0` (メートル) 程度に引き上げます。これにより、平面の裏側に仮想の分厚い判定ゾーンが作られ、突き抜けて裏側に達した粒子も「衝突した」と見なされて表側に引き戻されるようになります。 - Radius (パーティクル半径): 衝突判定用の球体の半径を
0.01(極小)から `0.05` 〜 `0.1` にわずかに拡張します。見た目のテクスチャサイズを変更せずに、判定のみを大きくすることで、接触の検出率を劇的に向上させます。
解決策B:Updateステージでの Sub-stepping (サブステップ数) の適用
パーティクルの移動速度が物理的に速すぎる場合は、VFX Graph全体の時間分割(シミュレーション精度)を向上させます。
- VFX Graphエディタで Update コンテキストブロックを選択します。
- インスペクターで Delta Time 設定を確認し、`Sub-stepping` オプションを有効にします。
- Sub-stepping Count を
2または3に設定します。
※これにより、1フレームの時間の更新を内部で細かく分割(例:3分割)して位置と衝突の計算を繰り返すようになるため、1回あたりの移動量が小さくなり、高速なパーティクルであってもすり抜けが完全に防げます(処理負荷は少し上がります)。
解決策C:速度ベクトルの最大値クランプ(C#連動なしで可能な最適化)
ノードグラフ内で、衝突する直前のパーティクルの Velocity(速度) を監視し、1フレームの最大移動量が物理的な床の厚みを超えないように制限します。Clamp Vector ノードや Absolute ノードを駆使して、速度の最大マグニチュードを $10.0$ 〜 $15.0$ 程度に抑えることで、ビジュアル上の爽快感を損なわずにトンネリングを防ぐことができます。
4. コリジョンデバッグ・チェックリスト
突き抜けが解決しない場合は、以下の項目を確認してください。
- バウンド減衰率 (Bounce): `Bounce` パラメータが `1.0` を超えていると、衝突の瞬間に反射速度が加速し、その次のフレームで猛烈な速度で対向の壁や床に突っ込んで突き抜けてしまいます。原則として `0.2` 〜 `0.8` の範囲に設定してください。
- Friction (摩擦係数) の設定: 粒子を床の上で滑らせる(Slide)設定にしている場合、床のコライダーの摩擦係数(Friction)を適切に設定し、衝突時に進行方向の速度が適度に減衰するように構成します。