[Unity] #07 線路 (鉄道)を、4点ベジェ曲線で自動生成(Bezier接続ツール化)

はじめに

Unity 5日目。

前回の記事の続きで、線路を直線に引いて、その上を列車が走る実装後、曲線を4点ベジェ曲線で実装したので、その備忘録メモです。

昨日は、「Grid and Snap」機能で実装しようとして挫折。

Three.jsでは、コードで曲線を生成しており、これをやると曲線の生成が自動で行えるほか、レールを自分で敷き詰める必要もなくなるので、 今後、地形に線路を張り巡らせる手間を考えるとコードで自動生成した方がどう考えても楽なのと、尚且つ、インセプターで使いまわしできる形にしておけば、 使い勝手も良くなるので、頑張って実装してみました。

AIに聞きつつやったとはいえ、数学の4点ベジェ曲線を使い実装しましたが、レールを思うようにつなぐことが出来ず物凄く苦労しました。

今回も動画を撮ってみました。

1. なぜレール生成ツールを作ったのか

Unity で線路を敷いていて一番ストレスだったのが、カーブ部分の手作業の調整だった。

直線レールなら並べるだけで済むが、カーブになると一気に作業が増える。

  • レール同士の「角度合わせ」が難しい
  • わずかにズレた接続が気になって何度も位置調整
  • 少しでも曲率が変わると、全部作り直し
  • カーブ → 直線 → カーブの連続が特に地獄

この作業が、シーンの規模が大きくなるほど時間を奪っていく。


そこで発想を変えて、

「始点レール」と「終点レール」を置くだけで、 その間を自然な曲線で自動接続してくれるツールが欲しい。

という結論に至った。

しかも、

  • 始点レールの「向き(forward)」
  • 終点レールの「向き(forward)」

この2つも考慮して、実際の鉄道のように“自然につながるカーブ”を作りたい。

Unity 標準にはこういう機能が無いので、 シーン編集中でも即結果が見える “エディタ拡張ツール” として実装することにした。

2. コンセプト

今回のレール生成ツールの発想は、とてもシンプルだ。

カーブの難しさは結局これに尽きる:

「始点レールがどこを向いているか?」 「終点レールがどこを向いているか?」

この2つを正しく考慮しないと、 レール同士が“自然につながらない”。

だからツールが扱う情報は、たったの 4 つだけ。

✔ 始点レールの位置(Transform.position)

✔ 始点レールの向き(Transform.forward)

✔ 終点レールの位置

✔ 終点レールの向き

この4点を使って、

両端のレールを “鉄道らしい滑らかなカーブ” で接続する。

という仕組みを作った。

やっていることはシンプルでも、 この4つの情報が揃うだけで Unity は自由自在に“カーブを描く”。

つまり、

  • カーブ角度の調整
  • 角度合わせの手間
  • 位置の微調整
  • 曲率変更による作り直し

これらを全部、ツールが自動で処理してくれる。

最終的には、

人間は「2つのレールを置く」だけ。
残りは全部 Unity が勝手にやってくれる。

そんな“完全自動敷設”のコンセプトにした。

3. 使用した技術のポイント

① Cubic Bézier(4点ベジェ)で曲線を生成

直線から曲線へ“自然につながる”レールを作るために、 3点ベジェ(2次)ではなく、4点ベジェ(3次) を採用した。

鉄道カーブのような “なめらかな立ち上がり” を再現するには、 制御点(P1 / P2)が 2つある Cubic Bézier が最適。

設定した各点は次のとおり:

  • P0 … 始点(始点レールの位置)
  • P3 … 終点(終点レールの位置)
  • P1 … 始点レールの forward に沿って伸ばした制御点
  • P2 … 終点レールの forward の反対方向に伸ばした制御点

そして P1 / P2 の距離は、

Circle Weight(0.552) × P0→P3 の距離

で計算している。

これは Illustrator や SVG が「円弧をベジェで近似する」時に使う公式と同じ。 鉄道カーブに必要な“自然さ”を作るには、この近似式が最も安定している。


② “Circle Weight = 0.552” という黄金比

Circle Weight(0.552)は、 円弧をベジェ曲線で近似する際の黄金比 として有名な値。

この値を変えるだけで、カーブの印象が大きく変わる:

  • 0.552 … 円弧に近い
  • 0.3〜0.4 … きついカーブ(急カーブ)
  • 0.7〜0.8 … ゆるやかなカーブ

スライダーで調整できるようにしたことで、 ゲーム向けの“雰囲気調整”が非常に簡単になった。


③ OnValidate によるリアルタイム生成

スクリプトのキモ。

Unity の Inspector で

  • 始点
  • 終点
  • CircleWeight
  • segmentLength

などの値を触ると、 即座にシーン内のカーブレールが再生成される。

これは Unity エディタ拡張でよく使われるテクニックで、 作業効率が劇的に上がる。

「値を変更 → カーブがリアルタイムに変わる」 この動作だけで“ツール感”が一気に増す。


④ DestroyImmediate で既存レールを削除

連続生成するとレールが無限に増えていくので、 生成前に必ず既存のレール(子オブジェクト)を削除している。

流れはこう:

  1. CurveGroup の子オブジェクトを全部消す
  2. 新しいカーブレールを並べ直す

つまりワンクリックで 完全クリア → 再敷設 ができる。


⑤ LookRotation(diff) でレールの「向き」を合わせる

ベジェ曲線が返すのは 位置(point) だけで、 向き(direction) は返さない。

そこで、次のようにして“接線方向”を求めている:

  1. 現在の t の位置(pos)
  2. ほんの少し先の t+Δt の位置(posNext)
  3. posNext - pos → 曲線の接線ベクトルになる

この接線を reference にして回転を作る:

Quaternion.LookRotation(接線, Vector3.up)

これにより、曲線に沿ってレールが正しい角度で並ぶ。

鉄道の “カーブ→直線” の自然な切り替わりを再現するための必須処理。

4. 実際のアルゴリズム(要所のみ)

今回のレール敷設ツールは、複雑な見た目とは裏腹に、 “核となる考え方” は非常にシンプル。

制御点(P1 / P2)の決め方と、ベジェ曲線の取り方さえわかっていれば Unity で誰でも再現できるレベルになっている。


制御点の計算(P1, P2)

始点と終点の forward をそのまま利用し、

P1 = P0 + forward(P0) * (距離 × Weight)
P2 = P3 - forward(P3) * (距離 × Weight)
  • P1 … “始点からどちらに曲がりたいか” を示す方向
  • P2 … “終点に向かってどの角度で戻りたいか” を示す方向

という意味になる。

Weight(0.552) で曲率をコントロールしているのがポイント。


各レールの位置

ベジェ曲線は 0〜1 のパラメータ t で滑らかに点を取れるので、

for t = 0 → 1:
    pos = CubicBezier(P0, P1, P2, P3, t)

この pos をそのままレールの配置位置に使う。

レールが多すぎると重くなるため、 実装側では segmentLength や railCount を利用して分割数を制御している。


レールの向き(接線ベクトルを使う)

ベジェは位置しか返さないため、 方向は「微小な先の点とのベクトル差」から求める。

posNext = CubicBezier(t + 小さな値)
direction = (posNext - pos).normalized
rotation = LookRotation(direction, Vector3.up)

この direction が曲線の接線になり、 レールの“向き”を決定する。

これにより、カーブの出口・入口も自然に接続される。


生成(Instantiate)

最後に、求めた位置+回転で一本一本レールを並べる。

Instantiate(railPrefab, pos, rotation, 親)

レール同士の位置ズレや回転ズレはすべて ベジェ曲線+接線計算 によって自動的に解決される。


全体まとめ

実際のアルゴリズムは:

  • 制御点計算
  • ベジェ曲線サンプリング
  • 接線ベクトルで向きを決定
  • Prefab を並べるだけ

という流れで、 100行に満たないコンパクトな実装で成立している。

「数学的に正しい接続」をやっているため、 長いカーブや鋭いカーブでも破綻がないのが最大のメリット。

5. 使い方

このツールの導入は非常にシンプル。 「始点」と「終点」を置いて、数値を調整するだけ。

  1. 空オブジェクトを作成し、名前を `“CurveGroup” とする
  2. そこに レール生成スクリプトをアタッチ
  3. railPrefab に レールのプレハブを割り当てる
  4. connectionRail に 始点レールの Transform
  5. endPoint に 終点レールの Transform
  6. circleWeight や segmentLength を調整して 曲率を変更
  7. シーン上に、カーブを描いたレールが即座に生成される

エディタ上で変数をいじるだけでリアルタイム更新されるので、 直感的に「カーブの形」を整えることができる。

6. まとめ

  • 始点と終点を置くだけで、自然な曲線レールを自動生成
  • レールの向き合わせ(回転調整)は完全自動
  • 曲率は スライダー1本でリアルタイム調整
  • 手動による角度合わせ・微調整が 完全に不要
  • Unity エディタ上で即結果が反映される、実用的なツールに仕上がった