VFX Graphを使って作成したエフェクトが、カメラの角度を少し変えただけでパッと不自然に消えてしまう現象は、初心者が最初につまずきやすい非常に典型的なレンダリングの不具合です。この現象は『バウンディングボックスによるカリング(Culling)』のメカニズムによって発生します。本記事では、このカリングの概念を身近な例え話で分かりやすく説明し、完全にバグを解消するための設定とスクリプト対策について解説します。
1. 例え話で理解する:『配送用の見えない梱包箱』の罠
この不具合を直感的に理解するために、『配送センターでの荷物のカリング判定』を例えに考えてみましょう。
あなたが運送会社(GPU描画エンジン)に対して、『とても長い蛇腹のおもちゃ(飛び散るパーティクル)』を配送したいとします。しかし、伝票を出す際に見えない梱包サイズとして『非常に小さな手のひらサイズのダンボール箱(デフォルトのBounds)』を申請してしまいました。
運送会社の仕分け人(Unityのカリング判定システム)は、カメラの視野という名の『ベルトコンベア』を見ています。仕分け人は伝票に書かれた『手のひらサイズの箱(Bounds)』だけを頼りに荷物の有無を確認します。コンベアが動き、手のひらサイズの箱の境界がスキャナ(画面外)を通り過ぎてコンベアの枠外へ外れた瞬間、仕分け人は『この荷物はもう仕分けレーンを出た(カリング対象である)』と判定し、配送処理を完全にストップします。しかし、実際にはその箱からはみ出た『蛇腹のおもちゃの胴体部分(実際に飛んでいるパーティクル)』がまだレーンに残っているはずですが、箱自体が画面外に消えたため、おもちゃ全体が荷下ろしされず、一瞬で消滅してしまうのです。
これが、VFX Graphにおける『カリング境界(Bounds)の不一致バグ』の正体です。Unityはパフォーマンス向上のため、見えない仮想の箱(Bounds)がカメラの視界に入っているかだけを監視し、その箱がカメラの枠から外れた時点で、中にいくら巨大なパーティクルが飛び散っていようとも、描画処理を完全にサボることで負荷を削ろうとします。梱包サイズが小さすぎるせいで、まだ見えているはずのパーティクルまで一括で廃棄されてしまうのです。
2. 解決策A:エディタ上でBoundsを手動で大きく設定する
最も簡単でパフォーマンス負荷もない解決法は、VFX Graphアセット側で、見えない梱包箱(Bounds)のサイズをあらかじめ実際に広がるパーティクルの範囲に合わせて手動で大きく再定義しておくことです。
設定手順:
- 対象のVFX GraphアセットをダブルクリックしてVFXエディタウィンドウを開きます。
- エフェクトの初期設定を行う『Initialize』ブロックまたはシステム全体の設定を表す『VFX System』ブロックを選択します。
- インスペクターウィンドウの『Bounds』セクションを確認します。初期状態では『Automatic(自動算出)』になっていることが多いですが、VFX GraphのAutomatic算出はバリアントの変化や重力、ノイズパラメータの動きを完全に予測できないため、小さすぎるサイズで固定されがちです。
- これを『Manual(手動)』に切り替えます。
- グラフ上に薄い水色のボックスフレーム(これがBoundsの梱包箱です)が表示されます。インスペクターの『Box Center』(中心点)および『Box Size』(大きさ)の値を調整し、パーティクルが風や重力で最も遠くに飛び散った場合でも、その全体が常に箱の中にすっぽり収まるサイズに拡大します。
※極端に大きくしすぎると、カメラに全く映っていない時にも描画の計算が走る原因になりパフォーマンスが低下するため、エフェクトの最大飛散半径の1.2倍程度に設定するのが最適です。
3. 解決策B:C#スクリプトから動的にBoundsを拡大する
動的な風のシミュレーションや、ゲームの状況によってパーティクルの挙動やスケールが極端に変化する場合、静的なBounds設定ではカバーしきれないことがあります。その場合は、C#スクリプトから動的に境界ボックスを更新します。
using UnityEngine;
using UnityEngine.VFX;
[RequireComponent(typeof(VisualEffect))]
public class DynamicVfxBoundsFix : MonoBehaviour
{
private VisualEffect vfx;
public Vector3 customBoundsSize = new Vector3(10f, 10f, 10f);
public Vector3 customBoundsCenter = Vector3.zero;
void Start()
{
vfx = GetComponent<VisualEffect>();
ApplyDynamicBounds();
}
void ApplyDynamicBounds()
{
if (vfx == null) return;
// C#からVFXのバウンディングボックスを直接設定する
// CenterはGameObjectからの相対座標、Sizeは梱包箱の縦横奥行きのサイズです
Bounds newBounds = new Bounds(transform.position + customBoundsCenter, customBoundsSize);
vfx.SetConceptBounds(newBounds);
}
// エディタのシーンギズモで設定範囲を見やすく表示
void OnDrawGizmosSelected()
{
Gizmos.color = Color.cyan;
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.DrawWireCube(customBoundsCenter, customBoundsSize);
}
}
このスクリプトをVFXオブジェクトにアタッチして実行することで、設定された十分な広さのバウンディングボックスが強制適用され、カメラをどのように回転させてもエフェクトが途中でパッと消えるバグを完全に防ぐことができます。