はじめに
Unity 5日目。
前回の記事の続きで、線路を直線に引いて、その上を列車が走る実装後、曲線を4点ベジェ曲線で実装したので、その備忘録メモです。
[Unity] #06: 直線レール敷設と列車の自走・停止
Unity 6で直線レールを敷設し、列車を移動させて駅手前で停止させるところまで実装。StopPoint運用、WaypointsへのTransform一括投入、Inspectorロック、スナップ設定の発見など、作業ログをThree.js経験者の視点で整理。
https://humanxai.info/posts/unity-06-train-move-stoppoint-straight-rail/昨日は、「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次) を採用した。
Bézier curve - Wikipedia
A Bézier curve bɛz.i.eɪ/ BEH-zee-ay, French pronunciation: [bezje]) is a parametric curve used in computer graphics and related fields.[2] A set of discrete control points defines a smooth, continuous curve by means of a formula. Usually the curve is intended to approximate a real-world shape that otherwise has no mathematical representation or whose representation is unknown or too complicated.
https://en.wikipedia.org/wiki/B%C3%A9zier_curve鉄道カーブのような “なめらかな立ち上がり” を再現するには、 制御点(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 で既存レールを削除
連続生成するとレールが無限に増えていくので、 生成前に必ず既存のレール(子オブジェクト)を削除している。
流れはこう:
- CurveGroup の子オブジェクトを全部消す
- 新しいカーブレールを並べ直す
つまりワンクリックで 完全クリア → 再敷設 ができる。
⑤ LookRotation(diff) でレールの「向き」を合わせる
ベジェ曲線が返すのは 位置(point) だけで、 向き(direction) は返さない。
そこで、次のようにして“接線方向”を求めている:
- 現在の t の位置(pos)
- ほんの少し先の t+Δt の位置(posNext)
- 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. 使い方
このツールの導入は非常にシンプル。 「始点」と「終点」を置いて、数値を調整するだけ。
- 空オブジェクトを作成し、名前を `“CurveGroup” とする
- そこに レール生成スクリプトをアタッチ
- railPrefab に レールのプレハブを割り当てる
- connectionRail に 始点レールの Transform
- endPoint に 終点レールの Transform
- circleWeight や segmentLength を調整して 曲率を変更
- シーン上に、カーブを描いたレールが即座に生成される
エディタ上で変数をいじるだけでリアルタイム更新されるので、 直感的に「カーブの形」を整えることができる。
6. まとめ
- 始点と終点を置くだけで、自然な曲線レールを自動生成
- レールの向き合わせ(回転調整)は完全自動
- 曲率は スライダー1本でリアルタイム調整
- 手動による角度合わせ・微調整が 完全に不要
- Unity エディタ上で即結果が反映される、実用的なツールに仕上がった
💬 コメント