ドナドナされるプログラマのメモ

Windows用アプリのプログラミングメモ

鉄道における縦曲線(縦断曲線)の値(曲率)

鉄道において線路の傾きである勾配が変化する場所には、カーブの縦バージョンである縦曲線(縦断曲線)を設ける。貨物ヤードシミュレータにも、ハンプを登場させるために縦曲線を作る事になったので、これの曲率を調べてみた。

結論から言うと、横方向のカーブの有無(程度)によって2パターンあるようだ。

  1. カーブの曲率がR=800より大(または、直線)の場合:縦曲線はR=3000より大。
  2. カーブの曲率がR=800以下の場合:縦曲線はR=4000より大。

上記はいくつかのサイトにあるものだが、困ったことにいずれも出典を明記しておらず真偽不明であった。そこでちょっと深堀りしてみたところ、昭和十二年の文書が見つかった。しかも、土木学会100周年記念アーカイブとなっており誰でも見れる!

稲田 隆「鉄道工学 上巻」誠文堂新光社, 昭和十二年, p.200

第5編 「縦曲線及び緩和曲線」 第16章「縦曲線」

http://library.jsce.or.jp/Image_DB/s_book/jsce100/pdf/24553/24553_05.pdf

f:id:donadonasan:20191008120449p:plain

日本における縦曲線

ドイツの場合という断り付きで、ハンプにおける縦曲線はR=100という記述もあった。ラッキー、これをつかおう。

f:id:donadonasan:20191008120619p:plain

ドイツにおける縦曲線

 関連ページ:

レールの曲率からFredoScale Radial Bendingで曲げる角度を計算する方法 - ドナドナされるプログラマのメモ

列車モデルの表現方法と操作

車両の連結状態を表す列車モデルについて、どのような構成として、連結や切り離し操作をどのように表現するか整理を兼ねてメモ。

まず、車両は以下のようなインターフェースで表現されているものとする。

public interface IVehicle{
public ICoupler couplers{get;}
public float Length {get;}
//  その他インターフェース
}

public interface ICoupler{
public Vector3 KnucklePos;
// その他インターフェース
}

 車両の連結状態を表す列車モデルに最低限必要なものは、連結器を表すオブジェクトである。一方、どのような車両がつながっているのかが分からないと不便なため、車両を表すオブジェクトも必要である。これを先頭から順番にリスト化すれば列車モデルになるように思われるが、IVehicleのcouplersはグローバル系における車両の前後を考慮した順番となっていない。そのため、車両の連結・切り離し判定をする際には総当たり判定が必要となってしまい、計算量が理想値の4倍に増えてしまう。これを解決するには、どのcouplerがどのcouplerと連結しているかを記録すればよい。そこで、下記のような構造体を新たに定義する。

struct Vehicle{
public ICoupler coupler; // a coupler linked to vehicle.couplers. coupler[0] to vehicle.couplers[0], coupler[1] to vehicle.couplers[1]
public IVehicle vehicle;
}

coupler[0]はvehicle.couplers[0]に連結されたcouplerを、coupler[1]は同couplers[1]に連結されたcouplerを参照している。これにより、計算量を理想値に等しくできる。ただし、連結されたcouplerが無い場合はnullとなるので、連結判定の前にnullチェックが必要となる。

列車は、上記構造体のListとなる。

List<Vehicle> train;

車両の切り離し判定をするには、Listの先頭から順次coupler間距離のチェックをすればよい。先頭車両において一方の連結器は何にもつながっていないことが保証されているので、先頭車両で切り離しが検知されたならばそれは先頭と2番目の間であることが保証される。そして、2番目の車両で切り離しが検知されたならば、それは2番目と3番目の間であることが保証される。以下同様にして、n番目の車両で切り離しが検知されたならば、それはn番目とn+1番目の間である。・・・昔、数学でこんな感じの証明をやったなあ。数学的帰納法だっけ?

この判定は下記関数にやらせる。

bool CheckDisconnection(Vehicle vehicle, int couplerNum){省略} // 切り離されたらtrue

ここまでをまとめると、以下のようになる。

foreach(Vehicle vehicle in train){
if(CheckDisconnection(vehicle, 0) || CheckDisconnection(vehicle, 1)){//切り離し処理};
}

次の問題は、「切り離し処理」をどうするかだ。切り離しが検知されたならば、列車を動力車側とそれ以外に分割し、動力車側を新たに列車とする。このためには、列車における動力車の位置を別途記憶する必要がある。

private int playerPos;  // train中の動力車の位置(=プレイヤーの位置)

困ったことに、ここでリスト中の位置情報が必要となってしまった。for文にすることも考えたが、List型の場合[]演算子での参照はちょっぴり遅そうだ。foreachを使い続けることにする。foreachを使いつつリストを操作することはできないので、foreach文中では残すべきtrainの範囲をチェックし、ループ外にて不要部分を削除することにする。

実装はまたこんど。

車両の管理方法

連結器のGUIを作ろうとしたけど、課題が見つかってしまい解決策を考え中。どんな課題かというと、この貨物ヤードシミュレータでは最終的に数十両もの貨物車両がいることになるので、連結器の数は下手すると100個以上。これ、どうやってGUIに表示すればいいんだ?一度に1個だけ表示するにしても、どうやって操作したい連結器を速やかに探し出すんだ?

こういうときによく使う解決策は、グループに分けるというもの。例えば、互いに連結された車両は列車とみなし、列車選択→車両選択とすれば選択がかなり楽になりそうだ。ただ、問題は連結判定にある。今回は、こだわりの都合で車両間の連結は完全に物理シミュレーションによっている。そのため、連結されているかどうかの判断はナックル同士の当たり判定に頼るか、車両同士の距離で判定するしかない。ナックル同士の当たり判定は処理が重いので、車両同士の距離で判定するほうがマシではある。しかし、例えば車両が50両あった場合、距離判定は50×49 = 2450回。フレームごとにやるのはちょっと気が引ける量である。

あと、今のシミュレータの初期状態のように、各貨車がバラバラになっていると貨車の数=列車の数になってしまい解決にならない。

ならばどうすればよい?こういう場合の常道は、操作対象をユーザーが操作したいグループのみに絞るという方法だ。このシミュレータの場合、ユーザーが操作したいのは動力車がつながっている列車のみだろう。そして、ユーザーは一度に1両の動力車しか操作できない(という仕様にする予定)。ならば、動力車を起点として、動力車につながっている車両をリストアップすればよい。列車に所属しているかのフラグで枝刈りをすれば、距離判定の回数は最悪でも50+49+・・・1 = 50*(50+1)/2 = 1275回、1編成あたり10両の場合で465回。かなり減った。

列車は線路の上に1次元で並んでいることと前回の判定結果をうまく活かせば、更に判定回数を減らせる気がする。前回列車に含まれていた車両については、隣り合う車両の距離を計測すれば分離判定ができる。あとは両端の車両と未連結の車両との距離をチェックすればいい。このやり方だと、たとえば10両編成ならば隣り合う車両の距離計測で9回、両端の車両と未連結の車両の距離計測で40×2=80回。合計、たったの89回。当初の1/27になった。

ただ、これだけだと横に並んでいるだけの車両が連結判定を受けてしまう。なので、最終的には連結器同士の距離を確認する必要がある。これはワーストケースで列車内の車両数×4回かかるが、先程のように直前の判定結果を活かせば列車内の車両数-1回で済むので問題ない。

あとは、表示方法と連結器開放の指示方法を決めるだけ。

うーん・・・画面下部に、テキストで

□DE10■■WAMU80000_01■■WAMU80000_02□

みたいに表示して、■をクリックすると連結器開放するみたいにしてみるか?難易度は高そうだけどわかりやすい気がする。

GUIの追加

今まで速度やノッチの状態、進行方向などはUnityのInspector上で見ていたんだけど不便なので、画面上にこれらを表示するGUIを実装してみた。

f:id:donadonasan:20190920221411p:plain

GUIを実装

だいぶ便利になった。あとは、各車両の連結器を開放するボタンを追加かな。

Unityで速度に応じ当たり判定を大きくしてみた

Unityで、高速移動をしたらRigidbodyの当たり判定をContinuousにしても非Rigidbody内のBoxColliderに当たらないケースがあった。そこで、速度に応じてBoxColliderの大きさを大きくするようにしてみたところ、成功。

youtu.be

これにより、無事に高速時でもレールの継ぎ目で生じるジョイント音を正しく再生できるようになった。

youtu.be