このスクリプトを使用すると、任意の形状でスプライトを分割/細分化し、生成された破片に物理ボディを割り当て、破壊エフェクトを作成できます。
破片のサイズ、物理特性、分割数、密度勾配、マテリアルなどを自由に設定できます。
オプションとして、一度分割された破片をさらに細分化(再切断)することも可能です。

boolean_sprite_cutter_with_tagged_shapes.gd (59.0 KB)
fragment_manager.gd (16.3 KB)
BooleanSpriteCutterWithTaggedShapesSampleProject.zip (635.2 KB)
注意: このスクリプトには GDScript Delaunay + Voronoi が必要です。
非常に複雑なため、サンプルプロジェクトのダウンロードを推奨します。
使用例 / サンプルプロジェクトの説明
0. 用語定義
| 用語 | 意味 |
|---|---|
| カッター (Cutter) | BooleanSpriteCutterWithTaggedShapes (主要な切断処理ノード; call_cut() メソッドを持つ) |
| 切断対象 | target_sprite_group 内の Sprite2D ノード (および、再切断が許可された破片 RigidBody2D ノード) |
| マスク形状 | shape_node_group 内の形状ノード (円/矩形/多角形など) |
| 内部破片 (Inside fragments) | マスクと重なる領域から生成された破片 (_fragment_type="inside") |
| 外部破片 (Outside fragments) | マスク外の残りの領域から生成された破片 (_fragment_type="outside") |
| マネージャー | FragmentManager (破片の生存期間と数の上限を自動管理; 通常、Autoload シングルトンとして保持) |
1. セットアップ (切断準備の整え方)
1-1. Voronoi 分割を使用するために gdDelaunay を有効にする
目的
カッターは、破片を「どのように割るか」を生成するために Voronoi パーティショニングを使用します。Voronoi 計算には res://addons/gdDelaunay/Delaunay.gd を使用するため、アドオンがプロジェクト内に存在し、プラグインが有効になっている必要があります。これが設定されていない場合、カッターは内部で Delaunay を読み込めず、切断処理が失敗します。
セットアップ手順 (自分のプロジェクトに追加する場合)
addons/gdDelaunay/をプロジェクトに配置します。プロジェクト > プロジェクト設定 > プラグインでgdDelaunayを有効にします。
サンプルプロジェクトの場合
res://addons/gdDelaunay/が含まれています。project.godotの[editor_plugins] enabledにres://addons/gdDelaunay/plugin.cfgがすでに登録されています。
1-2. 破片の自動管理のために FragmentManager を稼働させる (推奨)
目的
切断により多数の RigidBody2D 破片が生成されます。破片には物理演算、衝突判定、レンダリングが含まれるため、管理を放置すると負荷が簡単に増大します。FragmentManager を稼働させることで、以下の「クリーンアップ」を自動化できます。
- TTL (生存時間) 経過後に破片を削除
- 破片数が上限を超えた場合、古いものから削除
- 物理演算負荷を減らすため、減速したら破片を凍結 (Freeze)
- (オプション) 画面外に出た破片を削除
セットアップ手順 (サンプルプロジェクトと同様のスタイル)
fragment_manager.tscn(ルートNode2DにFragmentManagerスクリプトがアタッチされたもの) を準備します。プロジェクト > プロジェクト設定 > Autoloadに追加します。
- 名前 (Name) は、カッターから参照される名前に一致させる必要があります。
- パスは
fragment_manager.tscnの配置場所を指している必要があります。
サンプルプロジェクトの場合
- Autoload 名:
FragmentManagerSingleton - パス:
res://fragment_manager.tscn - これはカッターのデフォルト
manager_autoload_name="FragmentManagerSingleton"と一致しているため、追加設定なしで自動リンクされます。
1-3. カッター入力をトリガーできるよう InputMap を設定する
目的
カッターは入力待ちを行うノードではなく、call_cut() が呼び出されたときに切断を実行するノードです。したがって、任意のタイミング (プレイヤー入力、UI ボタン、イベントなど) で call_cut() を呼び出すための入口が必要です。最もシンプルな Godot 標準の入口は InputMap (Input Actions) です。
最小限のセットアップ (Godot 標準入力)
プロジェクト > プロジェクト設定 > Input Mapにアクションを追加します (例:call_cut)。- キーを割り当てます (例: Space)。
- 任意のノードに入力スクリプトをアタッチし、
call_cut()を呼び出します。
サンプルプロジェクトの場合
call_cutはすでにInputMapに登録されています。- Space はすでに
call_cutに割り当てられています。
1-4. 対象となる Sprite2D ノードを「ターゲットグループ」に追加する
目的
カッターは毎回、get_tree().get_nodes_in_group(target_sprite_group) を使用して候補を取得します。したがって、切断したい Sprite2D はすべてこのグループに含まれている必要があります。含まれていない場合、call_cut() を呼び出しても対象がゼロであるため、何も起こらないように見えます。
また、カッターは対象スプライトのテクスチャからアルファ値を読み取り、基本ポリゴン (切断可能な領域) を構築します。スプライトにテクスチャがない場合、またはアルファ閾値処理により不透明領域が抽出できない場合は、スキップされます。
手順
- 切断したい
Sprite2Dを選択します。 - ノードの [Groups] に
SpriteGroup(またはカッターで設定されたグループ名) を追加します。 Sprite2D.textureが割り当てられていることを確認します。
サンプルプロジェクトの場合
res://sample.tscnにおいて、Eggはgroups=["SpriteGroup"]に含まれています。
1-5. マスク形状ノードを「マスクグループ」に追加する
目的
カッターは shape_node_group グループから「マスク形状」を取得し、各形状に対して重なる切断対象を探します。マスク形状が登録されていない場合、カッターは内外の領域を分類する基準を持たず、処理を進めることができません。
スクリプト内の分岐により対応形状が処理されます。典型的な対応タイプは以下の通りです。
Polygon2D/CollisionPolygon2DCollisionShape2D(CircleShape2D/RectangleShape2D/ConvexPolygonShape2D/ConcavePolygonShape2Dなど)
手順
- マスクとして使用する形状ノードを準備します (例:
CollisionShape2D+CircleShape2D)。 - Groups に
CutterShapeGroup(またはカッターで設定されたグループ名) として追加します。 - 切断したいスプライトと重なるように配置します。
- 位置が重ならない場合、「AABB が交差しない」とみなされスキップされます。
サンプルプロジェクトの場合
res://spritecutter.tscnにおいて、CollisionShape2D2はgroups=["CutterShapeGroup"]に含まれています。- 形状:
CircleShape2D,radius=135.004。 - マスク形状は
BooleanSpriteCutterWithTaggedShapesの子ノードとして配置され、シーン内の切断対象と重なるように位置調整されています。
1-6. シーンにカッターを配置する
目的
カッターは、以下のすべての操作のためにシーン内にノードとして存在することを前提としています。
- シーンツリーを介したグループ検索 (切断対象とマスク形状の収集)
to_global()等を使用して形状をワールド空間のポリゴンに変換- 対象スプライトのアルファ由来のローカルポリゴンを
to_global()を使用してワールド空間に変換 - 生成された破片を何らかのノードの子として追加 (spawn/parent モードによる)
- (オプション)
NodePathを介して爆発中心ノードを参照 (例:inside_explosion_center_node)
したがって、スクリプトファイルがあるだけでは不十分です: シーン内にカッターノードを配置し、切断対象、マスク、爆発中心が参照可能であることを確認する必要があります。
手順 (サンプルと同じ構成)
- シーン内にカッターノード (
Node2D) を配置します。 boolean_sprite_cutter_with_tagged_shapes.gdをアタッチします。- カッターの子ノードとしてマスク形状ノード (
CutterShapeGroup) を配置します。
- これにより、カッターとマスクを一緒に移動しやすくなります。
- 爆発中心を使用する場合、カッターの子ノードとして
Node2D(例:ForceCenter) を配置し、inside_explosion_center_node等で参照します。
サンプルプロジェクトの場合
res://spritecutter.tscnには、スクリプトがアタッチされたBooleanSpriteCutterWithTaggedShapesという名前のNode2Dが含まれています。ForceCenterはカッターの子ノードであり、inside_explosion_center_node=NodePath("ForceCenter")で参照されています。- マスク形状
CollisionShape2D2もカッターの子ノードであり、CutterShapeGroupに登録されています。
2. 実行 (操作方法)
2-1. 切断を実行する
フロー
- Space キーを押すと、
call_cut()が呼び出されます。 - カッターは
SpriteGroupから対象候補を収集します。
Sprite2Dの場合、アルファから基本ポリゴンを生成します。- すでに切断済みのスプライト (
_already_cut=true) はスキップされます。
- カッターは
CutterShapeGroupからマスク形状を収集し、各形状に対して:
- AABB 交差 → 矩形ポリゴン交差 → 基本ポリゴン交差でフィルタリング
- 実際に交差している対象のみが破片化されます
- 内部領域は Voronoi 細胞で細分化され、ブーリアン交差により破片が生成されます。
- 外部領域はマスクでクリップされ、破片が生成されます。
- 生成された破片は
RigidBody2Dとしてシーンに追加され、インパルス/トルクを受けます。 - 元が
Sprite2Dであった場合、非表示になります (visible=false,_already_cut=true)。
サンプル操作
- 実行 (F5)。
- Space キーを押す (Input Action
call_cut)。 Eggが消え、破片が飛び散ります。
2-2. 破片が消える (マネージャー有効時)
目的
破片を放置すると蓄積します。サンプルではテスト中に負荷が簡単に増大しないよう「一定時間後に削除」を有効にしています。これは純粋なビジュアル効果というより、運用上の安全策です。
サンプルの場合
FragmentManagerSingletonが Autoload として稼働しています。fragment_manager.tscnで TTL が有効になっています:inside_ttl_seconds = 10.0outside_ttl_seconds = 10.0
- したがって、破片は生成から約 10 秒後に削除されます。
3. カッター設定 (サンプル値)
3-1. 「何を / どのような形状で切断するか」
| 項目 | 目的 (影響するもの) | サンプル値 (spritecutter.tscn) |
|---|---|---|
target_sprite_group |
切断対象が収集されるグループ。このグループに含まれないスプライトは処理されません。 | デフォルト ("SpriteGroup") |
shape_node_group |
マスク形状が収集されるグループ。空の場合、内外を判定できず処理が進行できません。 | デフォルト ("CutterShapeGroup") |
切断されない一般的な理由
- 対象が
SpriteGroupに含まれていない。 - 形状が
CutterShapeGroupに含まれていない。 - 対象スプライトにテクスチャがない。
- マスクと対象がワールド空間で重ならない (AABB が交差しない)。
3-2. 破片の粒度 (Voronoi)
| 項目 | 目的 | サンプル値 (spritecutter.tscn) |
|---|---|---|
voronoi_seed_count |
Voronoi シードの数。増やすと、より多く/小さな内部破片が生成される傾向があります。 | 20 |
voronoi_seed_density_mode |
シード分布のバイアス。中心に向かって密度を高くすると、「中心付近で細かいひび割れ」が生成される傾向があります。 | 1 (TowardMaskCenter) |
負荷と視覚効果に関する注意
voronoi_seed_countを増やすと、ブーリアンポリゴン交差が増え、負荷が増加する傾向があります。- 「どこで細かく分割するか」にバイアスをかけたい場合は、数を過度に増やすのではなく、密度モードを使用してください。
3-3. 飛び散り方向 (内部 / 外部)
| 項目 | 目的 | サンプル値 (spritecutter.tscn) |
|---|---|---|
inside_force_base_strength |
内部破片の飛び散り強度 (基本インパルス) | 1300.0 |
inside_force_direction_mode |
内部方向の選択 (固定ベクトル / 爆発中心からの放射状) | 1 (ExplosionFromPoint) |
inside_explosion_center_node |
内部の爆発中心 (NodePath) |
NodePath("ForceCenter") |
outside_force_direction_mode |
外部方向の選択 | 1 (ExplosionFromPoint) |
サンプルの爆発中心
ForceCenterはカッターの子ノードです。ForceCenter.position = (0, 134)。- 爆発方向は中心から破片へ (外向き) であり、飛び散り効果を生み出します。
3-4. 外観 (縁取り線)
| 項目 | 目的 | サンプル値 (spritecutter.tscn) |
|---|---|---|
draw_edge_line |
Line2D で破片ポリゴンの輪郭を描画し、ひび割れを見やすくする。 |
false |
注意
- 縁取り線は破片ごとに 1 つの
Line2Dを追加し、破片数が多いと描画コストが増加します。 - サンプルではパフォーマンスと簡素さを優先するため、無効にしています。
4. FragmentManager 設定 (サンプル値)
4-1. 生存期間 (TTL)
| 項目 | 目的 | サンプル値 (fragment_manager.tscn) |
|---|---|---|
inside_enable_ttl / inside_ttl_seconds |
内部破片が永遠に残らないよう、固定時間後に削除する。 | true / 10.0 |
outside_enable_ttl / outside_ttl_seconds |
外部破片を固定時間後に削除する。 | true / 10.0 |
注意
- TTL は物理演算が落ち着いているかどうかではなく、生成からの経過時間を基準としています。
- エフェクトのために破片を長く残したい場合は、秒数を増やしてください。
- 永遠に残したい場合は
*_enable_ttl=falseに設定しますが、負荷問題を避けるため最大数制限や凍結などを組み合わせることを推奨します。
4-2. 落ち着いたら凍結 (サンプルでは無効化済み)
| 項目 | 目的 | サンプル値 (fragment_manager.tscn) |
|---|---|---|
inside_enable_freeze_on_settled |
十分に落ち着いたら内部破片を凍結し、物理演算更新を減らす。 | false |
outside_enable_freeze_on_settled |
外部破片についても同様。 | false |
inside_disable_collision_when_frozen |
凍結後、衝突レイヤー/マスクを 0 に設定し、衝突コストを減らす。 | false |
outside_disable_collision_when_frozen |
外部についても同様。 | false |
凍結を有効にする場合
- 「破片が地面に落ちて止まるが、止まった後は物理演算更新を減らしたい」場合に有用です。
- 凍結後、破片は外部から力を加えない限り動きません。
5. 修正手順 (一般的なユースケース)
5-1. 切断対象を追加する
目的
同じカッターで複数のスプライトを切断できるようにします。カッターはグループ経由で収集するため、対象の追加は低コストです。
手順
- 切断したい
Sprite2Dを選択します。 - Groups に
SpriteGroupを追加します。 - マスク形状と重なることを確認します。
サンプルの場合
SpriteGroupに含まれているのはEggのみであるため、Eggのみが切断されます。
5-2. マスク形状を追加する (複数のマスクで切断)
目的
複数の形状を配置し、「各形状と重なる対象のみを切断する」ようにします。カッターは CutterShapeGroup 内のすべての形状を順次処理します。
手順
- 追加の形状ノードを作成します (例: もう一つの
CollisionShape2D)。 - Groups に
CutterShapeGroupとして追加します。 - 切断対象と重なるように位置を調整します。
サンプルの場合
CutterShapeGroupに含まれているのはCollisionShape2D2(円) のみです。
5-3. 破片を細かくする
目的
より「粉々になった」印象を与えます。主に内部破片に影響します。
手順
voronoi_seed_countを増やします。- 必要に応じて
voronoi_seed_density_modeとvoronoi_seed_density_powerでバイアスを調整します。 - パフォーマンスに問題がある場合、
simplify_toleranceを増やして基本ポリゴンの頂点数を減らします。
サンプルの場合
voronoi_seed_count = 20。
5-4. 破片を早く消す / 長く残す
目的
エフェクトの持続時間とパフォーマンスのバランスを取ります。サンプルではテスト中の蓄積を避けるため 10 秒を使用しています。
手順
fragment_manager.tscnを開きます。inside_ttl_secondsとoutside_ttl_secondsを調整します。- 永遠に残したい場合は
*_enable_ttl=falseに設定します (最大数制限や凍結との組み合わせを推奨)。
サンプルの場合
- 内部・外部ともに
ttl_seconds = 10.0です。
6. トラブルシューティング
| 症状 | 考えられる原因 | 解決策 |
|---|---|---|
| Space キーを押しても何も起こらない | InputMap に call_cut がない、またはキーバインドが間違っている |
Input Map に call_cut を作成し、Space を割り当てる |
call_cut() が呼び出されるが切断されない |
対象 Sprite2D が SpriteGroup に含まれていない |
対象スプライトの Groups に SpriteGroup を追加する |
call_cut() が呼び出されるが切断されない |
マスク形状が CutterShapeGroup に含まれていない |
形状ノードの Groups に CutterShapeGroup を追加する |
| 切断されない / 時々しか切断されない | マスクと対象が重ならない (AABB が交差しない) | 位置/スケール/回転を調整して明確に重なるようにする |
| 破片が無限に増える | FragmentManager が稼働していない / Autoload 名が一致しない |
Autoload FragmentManagerSingleton="res://fragment_manager.tscn" を設定する |
| 破片の負荷が高い | voronoi_seed_count が高い / 縁取り線が有効 / 破片が残りすぎている |
voronoi_seed_count を下げる、draw_edge_line を無効にする、TTL を短くする |
| 破片がすぐに消える | TTL が短すぎる | inside_ttl_seconds / outside_ttl_seconds を増やす (サンプルは 10 秒) |