概要
PathRibbonMesh2D は、指定された Path2D.curve をベイクして取得した一連の点に沿って、リボン形状の2Dメッシュを生成します。
このスクリプトを使用すると、パスに沿って伸びる蔓や触手などのエフェクト、攻撃の軌跡エフェクトなどを作成できます。
エフェクト中にZ順序を変更して描画順序を調整することで、オブジェクトに貫通したり、巻き付いたりする表現が可能です。



path_ribbon_mesh_2d.gd (54.9 KB)
tentacle_texture.zip (570.3 KB)
tentacle_project.zip (2.2 MB)
機能
レンダリングは、このノードの子として作成される自動生成された MeshInstance2D によって行われます。このスクリプトは、表示範囲の指定、幅変化カーブ、UVスケーリングと回転、鋭角でのミター処理、位置に基づく z_index の段階的な切り替えをサポートしています。
このスクリプトは、生成された MeshInstance2D ノードをスクリプト管理オブジェクトとして扱い、設定の変更や分割条件の変更時に再生成または破棄を行います。
前提条件と要件
これは Godot 4.x の2Dノード構成を対象に設計されています。
必要な条件は以下の通りです。
path_nodeにPath2Dが割り当てられている必要があります。path_node.curveはCurve2Dとして設定されている必要があります。- ベイクされた点リストには、少なくとも2つの点が含まれている必要があります。
視覚的な外観は ribbon_texture と blend_material に依存します。テクスチャが割り当てられていなくてもメッシュ自体は生成されますが、最終的な外観はマテリアルの実装に依存します。
セットアップ
Path2Dを配置し、そのCurve2Dを編集して形状を作成します。- このスクリプトを
Node2Dにアタッチします。 - ステップ1の
Path2Dをpath_nodeに割り当てます。 widthを設定します。- 視覚的な外観が必要な場合は、
ribbon_textureとblend_materialを設定します。 - 必要に応じて、表示範囲、太さカーブ、UV設定、鋭角処理、Zキーフレームを設定します。
生成ノード仕様
MeshInstance2D がこのノードの子として自動的に生成されます。生成されたノードには _path_ribbon_mesh2d_generated というメタデータがあり、スクリプトによって管理されます。
use_center_split = trueの場合、2つのメッシュ(上側と下側)が生成されます。ノード名はRibbonUpper_###とRibbonLower_###です。中心線を共有し、上側は中心から上方へ、下側は中心から下方へ展開し、三角形を構築します。use_center_split = falseの場合、1つのメッシュが生成されます。ノード名はRibbonSingle_###です。リボンの表面は、上辺と下辺を接続して構築されます。
z_keyframes_percent_and_z により z_index が変更される位置では、セグメントが分割され、複数の MeshInstance2D ノードが生成される場合があります。境界点は共有され、目立つ継ぎ目を減らします。
使用例
| 目標(ユースケース) | 設定 | 結果 | 備考 |
|---|---|---|---|
| パス全体を表示 | start_percent=0, progress_percent=100, slide_offset_percent=0 |
パス全体にリボンが表示される | width_curve が一定の1.0の場合、幅は一定になる |
| 先端を延ばす | start_percent を固定し、progress_percent を0→100でアニメーション |
セクションの端が伸びる | progress_percent < start_percent の場合、start_percent にクランプされ、逆方向のセクションは作成されない |
| 長さを固定して位置のみを移動 | 表示長さを progress_percent-start_percent で設定し、slide_offset_percent を変更 |
セクションがスライドしながら表示長さを維持 | 開始位置は 0..(100-VisibleLength) に制限される |
| 太さの変化をパス上の絶対位置に固定 | thickness_domain_mode=FullPathAbsolute、width_curve を設定 |
表示範囲が変わっても、同じ距離位置の太さは同じまま | 部分表示やスライド中も、太さの位相は固定されたままになる |
| 太さの変化を表示範囲に追従させる | thickness_domain_mode=VisibleRangeNormalized、width_curve を設定 |
表示範囲を0..1に正規化して太さが決定される | 表示範囲を変更すると、太さの位相も移動する |
| 上側と下側で異なる太さ比を使用 | use_center_split=true、upper_width_curve / lower_width_curve に異なる形状を設定 |
中心線に対して上下で展開が異なる | 上側のみ細く、または下側のみ太くすることも可能 |
| 進行方向に沿ってテクスチャを繰り返す | ribbon_texture を設定し、uv_scale.x を増加 |
U方向のタイル数が増加 | マテリアルとテクスチャ設定に依存する |
| テクスチャの角度を変更 | texture_rotation_degrees を設定し、必要に応じて uv_rotation_mode=TileLocalWrapRotation を設定 |
UVが回転し、パターンの角度が変更される | タイル化に使用する場合、タイルローカルの回転が有利な場合がある |
| 位置によって描画順序を切り替え | z_keyframes_percent_and_z に複数の Vector2(percent,z) エントリを設定 |
パスに沿って z_index が段階的に切り替わる |
Zが変更される箇所でセグメントが分割され、描画順序が安定化する |
| 滑らかさとコストのバランス | 滑らかさのために bake_interval を小さく、コスト低減のために大きく設定 |
カーブの忠実度と更新コストのトレードオフ | 重複する点が多い場合、重複排除により頂点生成コストを削減できる |
プロパティ一覧
| プロパティ | 型 / デフォルト | 説明(動作 / 計算 / 備考) |
|---|---|---|
path_node |
Path2D / (未設定) |
リボン生成のソース Path2D。path_node == null の場合、生成されたすべてのメッシュが非表示になる。path_node.curve == null の場合、エラー(push_error)を報告し、同様に非表示になる。スクリプトはカーブ参照の置換(例:Curve2D インスタンスの置換)を監視し、置換を検出するとベイクキャッシュを破棄してメッシュ更新をスケジュールする。 |
width |
float / 40.0 |
基本幅(ピクセル)。各点の「幅係数」(width_curve など)で乗算し、その後2で割って法線方向に頂点をオフセットする。センター分割の場合、「中心→上側」と「中心→下側」のオフセットが個別に計算される。負の値はクランプされないため、実用上は >= 0 として扱うべきである。 |
uv_scale |
Vector2 / (1,1) |
UV(テクスチャ座標)に適用されるスケール。適用方法は uv_scale_apply_mode に依存する。Uは線上の進行方向(表示範囲内での0..1)、Vはリボン全体(0..1)である。センター分割の場合、中心線はV=0.5を「中心線」として使用する。 |
bake_interval |
float / 1.0 |
curve.get_baked_points() を取得する前に Curve2D.bake_interval を設定する。値が変更された場合やカーブが置換された場合、ベイクキャッシュ(点と累積距離)が再生成される。値が小さいほど点の数と滑らかさが増すが、更新コスト(点処理+メッシュ生成)が高くなる。 |
start_percent |
int / 0 |
表示範囲の開始位置(0〜100)。_process で0..100にクランプされる。実際の表示計算時には、progress_percent に対して「開始 > 終了」にならないように調整される(以下参照)。 |
progress_percent |
int / 100 |
表示範囲の終了位置(0〜100)。0..100にクランプされ、progress_percent < start_percent の場合は start_percent に強制的に一致させられる。つまり、逆方向のセクションは作成されず、少なくとも「長さ0」のセクションになる。 |
slide_offset_percent |
int / 0 |
表示長さを保ったまま開始位置のみを移動する。内部処理:span_p = progress-start(0..100)を計算し、s_p = start + slide_offset を 0..(100-span_p) に制限する。終了位置は e_p = s_p + span_p となる。これにより「固定長、移動位置」が保証される。 |
sharp_bend_mode |
bool / false |
コーナー処理を切り替える。false の場合、前後の方向ベクトルをブレンドして中心方向を構築し、その法線を使用してリボンを展開する(より穏やかなコーナー成長)。true の場合、前後の法線をブレンドしてミター方向を構築し、join_scale(ストレッチ係数)を計算する。鋭い角度では大きくストレッチされるため、miter_limit が実質的な安全上限として機能する。 |
ribbon_texture |
Texture2D / (未設定) |
生成された各 MeshInstance2D.texture に割り当てられる。さらに、shader_texture_param_name が空ではなく、マテリアルが ShaderMaterial の場合、同じテクスチャがシェーダーパラメータとして設定される(以下参照)。テクスチャがなくてもメッシュは生成されるが、外観はマテリアル設定に依存する。 |
blend_material |
Material / null |
生成された各 MeshInstance2D.material に適用されるソースマテリアル。null の場合、マテリアルは割り当てられない。エディタでは、changed シグナル(参照されるマテリアルが編集されたとき)を監視し、変更時に再適用をスケジュールする。 |
material_unique_per_instance |
bool / true |
true の場合、blend_material.duplicate(false) によりローカルマテリアルを作成し、インスタンスごとに割り当てる。false の場合、blend_material の参照をそのまま共有する。共有すると編集が他のオブジェクトに影響するが、複製すると効果が隔離される代わりにマテリアル数が増加する。 |
shader_texture_param_name |
StringName / "" |
ローカルに適用されるマテリアルが ShaderMaterial の場合にのみ使用される。空でない場合、set_shader_parameter(shader_texture_param_name, ribbon_texture) を呼び出す。シェーダーがそのuniformを定義していない場合、設定しても効果がない(または警告が出る)ため、名前はシェーダーの実装と一致させる必要がある。 |
texture_rotation_degrees |
float / 0.0 |
UVを回転させる。0の場合、回転はスキップされる。回転中心は (0.5, 0.5) であり、方法は uv_rotation_mode に依存する。タイル化されたUV(U > 1 など)を使用する場合、外観が異なる可能性があるため、適切な uv_rotation_mode を選択すること。 |
uv_rotation_mode |
enum / LegacyWholeUVRotation |
UV回転モード。LegacyWholeUVRotation は、タイルの境界でアーティファクトが発生する可能性があるが、UV空間全体をそのまま回転させる。TileLocalWrapRotation は fposmod を使用して小数部(0..1)を抽出し、タイル内で回転してから整数タイルインデックスを復元する。これは「各タイル内で回転」になり、繰り返しテクスチャとよく機能する。 |
uv_scale_apply_mode |
enum / LegacyMultiplyVectorByUVScale |
UVスケールモード。LegacyMultiplyVectorByUVScale は Vector2(u, v) * uv_scale を単一の乗算として適用する。SeparateXYScale は uv_scale.x / uv_scale.y でUとVを個別にスケールする。センター分割における中心線(V=0.5)も同様にスケールされる。 |
miter_limit |
float / 2.5 |
sharp_bend_mode = true の場合、これはコーナーにおける join_scale(オフセット乗数)の上限値である。係数は前方法線とのドット積から導出されるが、非常に鋭い角度では無限大に近づくため、この上限は極端なストレッチを防ぐ。値が高いほどコーナーは鋭く/長くなり、値が低いほどコーナーの成長が抑制される。 |
width_curve |
Curve / Curve.new() |
幅係数の基本カーブ。各点について t_width を計算し、width_curve.sample(t_width) を係数として width に乗算する。サンプリング値が負の場合、0にクランプされる。_ready でカーブに点がない場合、(0,1) と (1,1) が自動的に追加され、デフォルトで「一定の幅」になる。 |
upper_width_curve |
Curve / Curve.new() |
センター分割(use_center_split = true)時の上側の幅係数カーブ。上側のオフセット距離は base_half * upper_factor * join_scale である。空の場合、_ready で一定の1.0に初期化される。 |
lower_width_curve |
Curve / Curve.new() |
センター分割時の下側の幅係数カーブ。下側のオフセット距離は base_half * lower_factor * join_scale である。空の場合、_ready で一定の1.0に初期化される。 |
thickness_domain_mode |
enum / VisibleRangeNormalized |
サンプリング位置 t_width の基準を切り替える。VisibleRangeNormalized の場合、現在表示されている距離範囲 [start_len, end_len] を0..1に正規化して t_width を計算する(表示範囲を変更すると、同じ絶対位置でも t_width が変化する)。FullPathAbsolute の場合、パス全体を0..1として扱い、t_width = dist / total_len を使用する(表示範囲を変更しても、同じ距離位置の t_width は変わらない)。 |
use_center_split |
bool / true |
メッシュ生成モード。true の場合、中心線を共有する2つのメッシュ(Upper/Lower)を生成する。上側は「中心→上側」から三角形を構築し、下側は「中心→下側」から構築する。false の場合、「上側→下側」を接続してリボン表面を形成する単一メッシュ(Single)を生成する。切り替え時に、生成ノード配列を再構築し、ウォッチャー(カーブ変更監視)を再接続する。 |
bake_point_dedup_enabled |
bool / true |
ベイクされた点の重複排除を有効にする。Curve2D.get_baked_points() の結果において、連続する点の距離が <= bake_point_dedup_epsilon の場合、重複が削除される。多くの小さな重複点を持つカーブにおける不要な作業(頂点生成/インデックス生成)を削減することを意図している。 |
bake_point_dedup_epsilon |
float / 0.0005 |
重複排除の距離閾値。実装は二乗距離を比較し、(p - last).length_squared() > eps^2 の場合のみ点を保持する。<= 0 の場合、実質的に無効になる。大きすぎるとカーブ形状が粗くなる。 |
profiling_enabled |
bool / false |
プロファイリングを有効にする。内部で Time.get_ticks_usec() を使用し、ラベルごとに合計時間と回数を累積する。 |
profiling_print_each_event |
bool / true |
profiling_enabled = true の場合、各イベントの測定結果を出力する。イベントごとの出力が不要な場合は false に設定する。 |
profiling_print_threshold_usec |
int / 0 |
イベント出力の閾値(usec)。<= 0 ですべて出力、>= 1 で閾値以上のイベントのみ出力する。重い部分のみ抽出する場合に有用。 |
profiling_dump_after_first_flush |
bool / true |
最初の _flush_updates 完了後にサマリー(合計/カウント/平均)を出力する。初期実行のみが必要な場合に有用。 |
debug_print_range_stats |
bool / true |
各メッシュ更新時、表示範囲(%)、距離範囲(開始/終了/合計)、U範囲、t_width 範囲、thickness_domain_mode を出力する。動作確認を目的としている。 |
z_keyframes_percent_and_z |
Array[Vector2] / [(0,0),(100,0)] |
Zキーフレーム。各要素は Vector2(percent, z) として扱われる。処理:(1) 作業用配列を作成するために percent を0..100にクランプ (2) percent 昇順でソート(同値の場合、z 昇順) (3) 任意の percent に対して、percent が <= その値である最後のキーを採用(ステップ) (4) z を丸めて int に変換し、MeshInstance2D.z_index に割り当てる。Zが変更される箇所でセグメントが分割され、継ぎ目を減らすために境界点が共有される。 |
典型的な運用パターン
- パス全体を表示する固定リボン
start_percent = 0、progress_percent = 100、slide_offset_percent = 0を設定し、width_curveを一定の1.0にすることで、一定幅のリボンを作成する。 - セクションを時間とともに延ばす
start_percentを固定し、progress_percentを0→100でアニメーションさせて表示範囲を延ばす。progress_percent < start_percentは許可されず、start_percentにクランプされる。 - 表示長さを固定して位置のみを移動
start_percentとprogress_percentの差が表示長さになる。slide_offset_percentを使用することで、その差を保ったまま開始位置のみを移動できる。移動可能な範囲は0..(100 - visible_length)に制限される。 - 太さの変化が表示範囲に追従するか、固定されるかを選択
thickness_domain_mode = VisibleRangeNormalizedの場合、サンプリングは0..1に正規化された表示範囲に基づいているため、表示範囲を変更すると太さの位相もシフトする。FullPathAbsoluteの場合、サンプリングはパス全体を0..1として使用するため、表示範囲が変わっても同じ距離位置の太さは同じままになる。