概要
PathRibbonMesh2D は、指定した Path2D.curve をベイクして得た点列に沿って、帯状の 2D メッシュを生成します。
本スクリプトを利用することにより、パスに沿って蔦や触手が伸びるようなエフェクトや攻撃の軌跡エフェクトを作ることができます。
エフェクトの途中でZorderを変更し描画順を調整することで何かに突き刺さったり巻き付いたりする表現が可能です。



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 系の 2D ノード構成を前提とします。
動作に必要な条件は次のとおりです。
path_node に Path2D が割り当てられていること。
path_node.curve が Curve2D として設定されていること。
ベイク後の点列が 2 点以上になること。
描画の見た目は ribbon_texture と blend_material に依存します。テクスチャ未設定でもメッシュ自体は生成されますが、表示結果はマテリアル側の実装に依存します。
セットアップ
Path2Dを配置し、Curve2Dを編集して形状を作成します。Node2Dに本スクリプトをアタッチします。path_nodeに 1 のPath2Dを割り当てます。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-表示長) に収まるように制御される |
| 太さ変化をパス上の位置に固定する | 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 を小さく、負荷優先なら大きくする |
曲線追従と更新コストのバランスが変わる | 重複点が多い場合は dedup 設定で頂点生成負荷を抑制できる |
プロパティ一覧
| プロパティ | 型 / 既定値 | 説明(挙動・計算・注意点) |
|---|---|---|
path_node |
Path2D / (未設定) |
リボン生成元。path_node == null の場合は全生成メッシュを非表示にします。path_node.curve == null の場合はエラーを出し(push_error)、同様に非表示になります。曲線参照が差し替えられた場合(Curve2D を別インスタンスに置き換えた等)を監視し、差し替え検知時はベイクキャッシュを破棄してメッシュ更新を予約します。 |
width |
float / 40.0 |
基準幅(ピクセル)。各点で算出される「幅係数」(width_curve 等)を掛けた上で半分にし、法線方向へオフセットして頂点を作ります。中心分割の場合は「中心→上」「中心→下」で別々のオフセットを作ります。負値が入ってもクランプ処理はしないため、運用上は 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 |
Curve2D.bake_interval に設定してから curve.get_baked_points() を取得します。値変更や曲線差し替え時はベイクキャッシュ(点列と累積距離)を再生成します。小さいほど点数が増え、曲線追従は滑らかになりますが、更新コスト(点処理+メッシュ生成)が増加します。 |
start_percent |
int / 0 |
表示区間の開始(0〜100)。_process 内で 0..100 にクランプします。実際の表示計算では progress_percent との関係で「開始>終了」にならないよう調整されます(下記 progress_percent 参照)。 |
progress_percent |
int / 100 |
表示区間の終了(0〜100)。0..100 にクランプされ、さらに progress_percent < start_percent の場合は start_percent に揃えます。つまり「逆方向の区間」は作らず、最短で「長さ 0」の区間になります。 |
slide_offset_percent |
int / 0 |
start_percent と progress_percent の差分(表示長)を維持しつつ、開始位置だけを移動します。内部的にはまず 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 の場合は shader parameter にも同テクスチャを設定します(後述)。テクスチャ未設定でもメッシュ自体は生成されますが、見え方はマテリアル設定に依存します。 |
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 は U と V を別々に uv_scale.x / uv_scale.y でスケールします。中心分割の中心線(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 の場合は 1 枚(Single)で「上→下」を結んで帯面を作ります。切替時は生成ノード配列を作り直し、監視(カーブ変更の watcher)も再接続します。 |
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 のとき、各イベントの計測結果を print します。イベント単位の出力が不要な場合は 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 を print します。挙動確認用です。 |
z_keyframes_percent_and_z |
Array[Vector2] / [(0,0),(100,0)] |
Z キーフレーム。要素は Vector2(percent, z) として扱われます。処理は次の通りです。① percent を 0..100 にクランプして配列を作成 ② percent 昇順(同値なら z 昇順)でソート ③ 任意の地点 percent に対し「percent 以下の最後のキー」を採用(ステップ) ④ z は round して 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 - 表示長)に収まるように制御されます。 - 太さ変化を表示区間に追従させるか固定させる
thickness_domain_mode = VisibleRangeNormalizedは表示区間を 0..1 に正規化してサンプルするため、表示区間を変えると太さ変化の位相も変わります。FullPathAbsoluteはパス全体を 0..1 としてサンプルするため、表示区間を変えても同じ距離位置は同じ太さになります。