🐍

【UE5】蛇をsplineカーブに沿わせて動かしてみた

に公開

はじめに

Mayaなどで馴染みのあるモーションパスのように、身体の長いキャラクターをカーブに沿わせて動かすことがUEでできるか調べてみました。同一の機能は見つけられませんでしたが同じようなことを実現する方法がありましたのでご紹介します。

キャラクターについて

ものすごく適当ではありますが蛇のようなモデルを用意しました。骨は13本で等間隔になるようにしています。最初はもっと少ない数で作ったのですが、今回行った実装方法だとカーブに沿いきれず身体の形状が崩れてしまったため多めにしています。

BPの実装

基本的な流れは以前書いた記事と同じです。splineカーブのBPとキャラクターのBPをレベルに配置して、キャラクターの方のBPで挙動の実装を行っています。ただし今回はキャラクターの骨をBPで動かしたいので、スケルタルメッシュコンポーネントではなくPoseableMesh ポーズ設定可能メッシュコンポーネントを使っています。
https://zenn.dev/hota12/articles/5e7fc31b69fbde

EventBeginPlay

1.splineカーブの長さを取得

splineのBPから「GetSplineLength」ノードを使ってカーブの長さを取得し、変数に記録しておきます。

2.キャラクター(蛇)の身体の長さを取得

以下のような流れでキャラクターの最初と最後の骨の間の距離を測り、変数に記録しておきます。
所々で使用しているPoseableMeshは変数のコンポーネントの中にあります。

  • 最初の骨の位置を取得
  • 「GetBoneName」で1番目の骨名を取得(作ったモデルの0はルートノードだったため)
  • 「GetBoneLocationbyName」で1番目骨の位置を取得
  • 「GetNumBone」で骨の総数を取得、-1して最後の骨の名前を取得
  • 「GetBoneLocationbyName」で最後の骨の位置を取得
  • 「Distance」で2点間の距離を測る

3.骨1つ1つの間の距離を取得

2で取得したキャラクターの長さを使って骨1つ分の長さを取得します。このモデルは骨を均一に配置しているため単純に骨数で割って距離をだしていますが、もし骨が均一ではないモデルだった場合は違う取得方法にする必要があります。

EventTick

1.進む距離を計算

1フレームで進む距離に関しては前回の記事と同じく、TickのDeltaSecondsとSpeedを掛けた数字をDistanceに足すという方法で割り出しています。

2.骨の位置をカーブ上に設定

「ForEachLoop」は配列型の変数を渡すと中身に対して順番に同じ処理を行うことができます。
ここではBones変数に頭から尻尾までの骨の名前をデフォルト値で入れてあり、ArrayElementに骨の名前が、ArrayIndexに何番目かが出力されます。

各骨の位置を設定するため、まずはをカーブ上の長さに対してどのくらいの距離に骨を配置すべきかを計算していきます。頭を配置する距離をベースとして、各骨の長さ分後ろに下げて距離を割り出します。以下が処理の流れになります。

  • 頭から見た子階層の順番でindexに数字が入ってきます
  • セグメントごとの距離を掛けることで、骨の距離を割り出します
  • このままだと頭より前に出てしまうので、-1を掛けて頭より後ろに下げる距離にします
  • Distance(頭の距離)を足すことで現在進んでいる位置からどのくらい後ろかの距離にします

この後使うノードはカーブ一周分の距離に対しての値が求められるため、2週目以降も距離の値が増え続けてしまうのは都合が悪いです。Wrapノードを使うことで値を修正しています。
例えばMax値(カーブ一周分の距離)を100に対して110(骨を配置するカーブ上の距離)が入力された場合、Wrapノードは10という値を返してくれます。

「GetTransformatDistanceAlongSpline」でカーブの長さに沿った距離を与えて、スプライン上のトランスフォーム値を取得します。本当はそのまま「SetBoneTransformbyName」でトランスフォームを設定すればいいのですが、今回使ったデータでは色々とうまくいかなかったので調整して値を渡しています。

  • splineカーブが地面上にあり、モデルが地面にめり込んだので少し上に上げています
  • そのままだと前を向いてくれなかったのでZに-90を加えています
  • スケールがすごく小さくなってしまったので、元の値をそのまま使っています。

Discussion