PathRibbonMesh2D の概要(触手テクスチャを含む)

概要

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

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_nodePath2D が割り当てられている必要があります。
  • path_node.curveCurve2D として設定されている必要があります。
  • ベイクされた点リストには、少なくとも2つの点が含まれている必要があります。

視覚的な外観は ribbon_textureblend_material に依存します。テクスチャが割り当てられていなくてもメッシュ自体は生成されますが、最終的な外観はマテリアルの実装に依存します。

セットアップ

  1. Path2D を配置し、その Curve2D を編集して形状を作成します。
  2. このスクリプトを Node2D にアタッチします。
  3. ステップ1の Path2Dpath_node に割り当てます。
  4. width を設定します。
  5. 視覚的な外観が必要な場合は、ribbon_textureblend_material を設定します。
  6. 必要に応じて、表示範囲、太さカーブ、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=FullPathAbsolutewidth_curve を設定 表示範囲が変わっても、同じ距離位置の太さは同じまま 部分表示やスライド中も、太さの位相は固定されたままになる
太さの変化を表示範囲に追従させる thickness_domain_mode=VisibleRangeNormalizedwidth_curve を設定 表示範囲を0..1に正規化して太さが決定される 表示範囲を変更すると、太さの位相も移動する
上側と下側で異なる太さ比を使用 use_center_split=trueupper_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 / (未設定) リボン生成のソース Path2Dpath_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_offset0..(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空間全体をそのまま回転させる。TileLocalWrapRotationfposmod を使用して小数部(0..1)を抽出し、タイル内で回転してから整数タイルインデックスを復元する。これは「各タイル内で回転」になり、繰り返しテクスチャとよく機能する。
uv_scale_apply_mode enum / LegacyMultiplyVectorByUVScale UVスケールモード。LegacyMultiplyVectorByUVScaleVector2(u, v) * uv_scale を単一の乗算として適用する。SeparateXYScaleuv_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 = 0progress_percent = 100slide_offset_percent = 0 を設定し、width_curve を一定の1.0にすることで、一定幅のリボンを作成する。
  • セクションを時間とともに延ばす
    start_percent を固定し、progress_percent を0→100でアニメーションさせて表示範囲を延ばす。progress_percent < start_percent は許可されず、start_percent にクランプされる。
  • 表示長さを固定して位置のみを移動
    start_percentprogress_percent の差が表示長さになる。slide_offset_percent を使用することで、その差を保ったまま開始位置のみを移動できる。移動可能な範囲は 0..(100 - visible_length) に制限される。
  • 太さの変化が表示範囲に追従するか、固定されるかを選択
    thickness_domain_mode = VisibleRangeNormalized の場合、サンプリングは0..1に正規化された表示範囲に基づいているため、表示範囲を変更すると太さの位相もシフトする。FullPathAbsolute の場合、サンプリングはパス全体を0..1として使用するため、表示範囲が変わっても同じ距離位置の太さは同じままになる。
「いいね!」 4

とてもクールな効果ですね!

「いいね!」 1