【UE5】Geometry Scriptでメッシュを動的にくり抜く方法

この記事は Unreal Engine (UE) Advent Calendar 2025 のシリーズ3の12月5日分の投稿となります。
プログラマーの尾関です。
今回は Geometry Script を使ってメッシュを動的にくり抜く方法を紹介します。
- 動作確認バージョン:UE 5.5.4
目次
- プロジェクト作成
- プラグインを有効化
- 不要な地形を削除
- Dynamic Mesh Actorを作成
- 穴を開けるメッシュのインポート
- BP_FirstPersonProjectileを編集
- Event Hitの編集
- その他
- 参考
プロジェクト作成
どんなプロジェクトでも良いですが、今回はファーストパーソンで作ってみます。

プラグインを有効化
プラグイン設定から "Geometry Script" を有効化します。

不要な地形を削除
残したままでも良いですが、ライフル (BP_Pickup_Riffle) とスタート地点 (PlayerStart) を除いて、いったん不要な地形を削除してみました。

Dynamic Mesh Actorを作成
メッシュに動的に穴を開ける場合には、Dynamic Mesh 同士とする必要があります。
まずは穴を開ける壁を Dynamic Mesh Actorで作成します。

アセット名は「BP_Dynamic」としました。

Construction Scriptで壁を作成
壁メッシュは Construction Script で作ります。

ノードグラフは以下のようになりました。

- Dynamic Mesh ComponentからDynamic Meshを取得
- Append Box with Collisionノードで、壁を作成 (Dimension X, Y, Z = 1000, 1, 500)
- Set Dynamic Mesh Collision from Meshノード でコリジョンを設定
- Update Collisionでコリジョンを更新
これをレベルに配置すると壁が作られます。

穴を開けるメッシュのインポート
今回は星型の穴をあけるようにしてみます。参考として作成したobjファイルを添付しておきます。

このファイルを Unrealエディタにドラッグ&ドロップ。
このデータですが回転せずにインポートすると、地面に寝そべってしまうので X軸を90度回転してインポートします。

アセット名は「SM_Star」としました。

メッシュエディタで中身を確認しておきます。

BP_FirstPersonProjectileを編集
コンテンツブラウザから「Projectile」などで検索して、「BP_FirstPersonProjectile」を開きます。

コンポーネントの追加
「+追加」をクリックして「Dynamic Mesh Component」と「Static Mesh」を追加します。

Dynamic Meshはメッシュをくり抜くために必要なメッシュで、Static Meshには先程の星型メッシュを設定します。
ということで Static Mesh Componentを選んで、”SM_Star” を設定します。

ただ、ビューポートで弾丸メッシュを確認すると星型メッシュの中に埋まってしまいます。

そこで星型メッシュの拡大・縮小を「0.01」にして星型メッシュが見えないようにします。

Event Hitの編集
Event Hitには弾丸の衝突処理が書かれていますが、イベント以外はすべて消します。

ここに衝突したときの穴あけ処理を書いていきます。

ポイントは以下の3つです。
- Hit Locationをローカル座標系に変換(ヒットしたところのローカル位置の計算)
- Static Meshを Dynamic Meshにコピー (星型メッシュを Dynamic Meshに変換)
- 穴あけ実行
Hit Locationをローカル座標系に変換(ヒットしたところのローカル位置の計算)
Hit Locationに得られる座標はワールド座標系ですが、穴あけを実行するにあたってはローカル座標系の方が都合が良いので、World Transfromを取得して、それに Inverse Transform Location を実行することでローカル座標への変換を行っています。

Static Meshを Dynamic Meshにコピー (星型メッシュを Dynamic Meshに変換)
Dynamic Mesh の Copy Mesh from Component ノードを使用すると、Static Meshから Dynamic Meshへの変換が可能です。

穴あけ実行
穴あけ実行には Apply Mesh Boolean (Geometry Script) を使用します。

Target Meshが穴を開ける対象のメッシュ (Dynamic Mesh) で、Tool Mesh が穴を開けるためのメッシュ (Dynamic Mesh) です。
Tool Transform Location に先ほど計算した衝突した位置を渡しています。
注意点として、Operation を「Subtract」にします。初期値の “Union” だと Tool Mesh を結合します。
さらにもう一つ、Option Fill Holes のチェックは外します。というのもApply Mesh Boolean は欠損したメッシュを自動で穴埋めする機能があるようで、ここにチェックが入っていると穴あけがうまくいかないことがあるからです。
なお、Tool Transform と Options は構造体なので、右クリックして「構造体品を分割」で分割して設定します。

では実行して確認。
星型でくり抜くことができました。

その他
処理コストの注意点
- メッシュのくり抜き処理 (EGeometryScriptBooleanOperation::Subtract) はかなり重たい処理です。頂点数が多くなるほどコストがかかり、繰り返し処理するとさらに重くなります
- 具体的には[球体」のくり抜きを3回ほど行うと、最悪の場合エディタが止まる可能性があります
そのため、くり抜くためのメッシュはなるべくシンプルな形状にする必要があります。
くり抜き位置がうまくあわない時の調査方法
ApplyMeshBooleanに渡す EGeometryScriptBooleanOperation は "Union" を選ぶと、くり抜きではなく「メッシュの結合」となります。

そのため、くり抜き位置が合わない場合は "Union" にすると位置のズレなどが確認しやすいです。
各メッシュ間のコピー
Dynamic Meshのみで完結する場合は不要ですが、実際のゲーム開発では Static Mesh で作成したメッシュを使ってくり抜きたい、といった需要があると思います。その場合「Static Mesh → Dynamic Mesh」といったメッシュ間のコピーには CopyMeshFromStaticMesh という関数を使うと便利です。
以下は簡潔なものですが、Unreal Engineが用意しているコピー関数をまとめてみました。

ただ、Dynamic Meshから Static Meshや Procedural Mesh にコピーする関数は用意されていないようで、自作する必要があるかもしれません。
あと Static Meshをコピーする際は "Allow CPU Access" をつけないと正常にコピーできないことがあるようです。

参考
今回の記事を書くにあたって以下の動画を参考にさせていただきました。
Geometry Scriptの公式ドキュメントは以下のページにあります

