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

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

並形自動連結器の実装(今度こそ終わりVer.)

色々苦労したけれど、並形自動連結器の実装が終わったはずなので一旦まとめ。

目次

並形自動連結器の外観モデル

シーマ(仮)さんのワム80000 mmdモデルより拝借。一旦Blenderに部品単位で取り込み、それぞれfbx形式でエクスポートして作成。

f:id:donadonasan:20190905235335p:plain

並形自動連結器の外観


並形自動連結器は本体、ナックル、ロックピン(錠)の3部品から成る。それぞれ、以下のような形状。

f:id:donadonasan:20190905235443p:plain

並形自動連結器本体の外観

f:id:donadonasan:20190905235842p:plain

並形自動連結器のナックル外観

f:id:donadonasan:20190905235945p:plain

並形自動連結器のロックピン(錠)外観

並形自動連結器の動作原理

連結器が開放されている状態では、ロックピン(下図の青線で囲まれているもの)はナックル後部の平らなところに乗っている。この状態では、ナックルは自由に回転することができる。

f:id:donadonasan:20190906000507p:plain

連結器が開放されている状態

しかし、ナックルが回転して連結器が閉じた状態になると、ロックピンを支えていたナックルの平らな部分が居なくなってしまい、ロックピンは下に落ちる。こうなると、ナックルはもう開けなくなりロックされる。このように、閉じると自動的にロックがかかるため自動連結器と呼ばれるらしい。

f:id:donadonasan:20190906000710p:plain

連結器が閉じている状態

ナックルは、連結相手のナックル(下図の青線で囲まれているもの)がロックピンを支えていた平らな部分に当たることで回転する。よくできている。

f:id:donadonasan:20190906001611p:plain

ナックルがナックルを回転させる

そして、連結されるとこのように前後左右どちらに動いても外れないようにナックルと本体がうまく噛み合う。

f:id:donadonasan:20190906001854p:plain

並形自動連結器の連結状態

物理モデル

以上の挙動をUnityで実装するには、多くの課題がある。最大の課題は、モノとモノの衝突判定の制約だ。Unityでは凹みを持つ形状(メッシュ)の当たり判定ができない(動くことのない、Staticなものを除く)。そのため、ナックルや本体のような凹みだらけで凹みが大事なものの衝突判定をするには、これを複数の凹みを持たない当たり判定に分割する必要がある。今回は、必要最低限の当たり判定を、複数の長方形やカプセル型の判定で実現することにした。

最低限必要な当たり判定とは、以下を実現するものである。

  1. ロックピンを支える当たり判定
  2. ロックピンに当たり、ナックルの回転を妨げる当たり判定
  3. 相手のナックルに当たり、相手のナックルを回転させる当たり判定
  4. 相手のナックルに当たり、自分を回転させる当たり判定
  5. 連結状態にて前後左右の挙動を制約する当たり判定
  6. ロックピンがすっぽ抜けないようにする当たり判定

これらのうち、1, 2は兼用可能、また4, 5の一部にもできそうである。そこで、大きく以下の当たり判定を作ることにした。

  1. ナックル後部の平らな板
  2. ナックル前部~側面の相手ナックルに当たる部分
  3. 本体のうち相手ナックルに当たる部分
  4. 本体のうちロックピンを受け止める部分
  5. ロックピン自身の当たり判定

その結果、以下のようになった。緑の線が当たり判定である。

f:id:donadonasan:20190906003027p:plain

並形自動連結器 本体の当たり判定

f:id:donadonasan:20190906003112p:plain

ナックルの当たり判定

f:id:donadonasan:20190906003246p:plain

ロックピンの当たり判定

ロックピンの当たり判定や、ナックル後部の当たり判定は連結時のガタツキをへらすために若干見た目からずらしている。

また、当たり判定以外にも各部品の相対位置や角度をJointにより制約している。このモデルでは計3つのジョイントを使っている。

  1. ロックピン - 本体間のConfigurableJoint
    ロックピンが本体に対し上下方向にのみ自由に動けるように制約している。
  2. 本体 - ナックル間のHingeJoint
    本体とナックルがY軸を中心とした回転のみできるように制約している。また、ロックピンが上がっているときには自然に開くよう弱いバネで回転させ、ロックピンが下がっているときには物理演算を安定させるため閉まるよう強いバネで回転させる。これは、強い力が加わると当たり判定同士がめり込み始め、最後にはすり抜けるという現象を防ぐためである。ばね定数の切り替えは、ロックピンの位置やナックルの開度をトリガにC#スクリプトで制御している。
  3. 本体 - 車体間のConfigurableJoint
    並形自動連結器は、一般に前後方向の衝撃を吸収する緩衝器と、左右方向の回転を許容するスプリング付き回転軸を持つ。これを実現するため、ConfigurableJointを用いた。

実装結果

こんな感じに動きます。なお、ロックピンを上げるにはハンドルを動かします。これも実装していますが今回は紹介を省略。

youtu.be

Unityでの回転の罠

Unityで、振り子のような挙動をするはずのモデルを作ったんだけど重力が働いていないような、無重力のような挙動をされて一晩はまってしまった。以下モデルの構成と現象、原因、対策。

モデルの構成

2つのGameObjectからなるモデル。一方のObject Aは空間に固定され、もう一方のObject Bは回転系のJointを介してObject Aにつながっている。挙動はHingeJoint, ConfigurableJointのどちらでも同じ。Jointの回転LimitはFree、つまり制約なし。

各ObjectにはTransform, RigidBodyとJointが含まれる。またMeshを有する子オブジェクトがある。

現象

Object Bには回転軸の制約がないので、重力により振り子のような挙動をするはず。なのに、やたらゆっくりとした動きをする。まるで重力が無いか、小さいように見える。

原因

近くにボールを置いてみたが、ボールはそれっぽい速さで落ちていった。故に重力は問題なし。となると慣性モーメントしか原因が無いのでぐぐってみると、Unityでは慣性モーメントを自動計算または手動設定できるが、自動計算する場合はColliderの形状を援用することが判明。今回のモデルではColliderを設定していなかったため、慣性モーメントを正しく自動計算出来ていなかったのだろうと推測し、試したところ大成功。以下、Collider有無で挙動がどう変わるかの動画。

youtu.be

対策

回転対称の大まかな形状に沿ったColliderを作る。

レールの曲率からFredoScale Radial Bendingで曲げる角度を計算する方法

毎回考えるのは面倒なのでメモ。

レールの曲率半径をR、レールの長さをdとし、これらからレールを曲げる角度βを求める。

このレールは円周:2πRの一部である。すなわち、円周長さを1としたとき、レール長さはd/2πRである。ゆえに、角度表記にすると、これは角度θ=360d/2πRの円弧である。

f:id:donadonasan:20190818230756p:plain

角度計算

Radial Bendingで使う角度は、もとの直線に対し何度曲げるか(上図のβ)なので、θからこれを求める。円弧と各直線は接しているので、円の中心から接点に伸ばした直線と接線のなす角度は90°。ゆえにαは180-θ。ゆえにβ=180-α=θ。シンプルである。

ゆえに、β=360d/2πR。

線路曲げメモ

SketchUp!で下記自作線路を曲げる際のメモ。

枕木は34本あるので、間の33箇所を曲げる。厳密にはペーシも曲げなきゃいけないけど、位置合わせが大変になりそうなので割愛する。

後の作業のため、真ん中に補助線を入れて全体をグループ化する。

その後、FredoScaleよりRadial Bendingを選択し、R=107ならば13.1°曲げる。TABキーで分割数を変更し忘れないように。

シングルスレッドで頑張りやがるため、数十分放置したら出来上がり。

自動連結器

今日は自動連結器づくり。できればすべて物理シミュレーションで動くようにしたいけど、さすがに無理かなあ。でもまずはチャレンジ。以下、メモ。

可動範囲

自動連結器は前後・左右方向に緩衝器がある。しかし、この可動範囲が分からない。日本大百科全書の図から、前後左右それぞれナックル1個分にしてみようか。

kotobank.jp

ロック(錠)

ロックピンは、ナックルの動きを邪魔したり開放したりすることでロック機能を実現する。これだけならUnityでシミュレーションできる気がするけど、並形自動連結器のロックピンはロック解除するとナックルが開くまでピンが解除位置に留まるという機能がある。

f:id:donadonasan:20190816154150p:plain

ロックピン端部の形状(https://www.youtube.com/watch?v=Z8gd4jh_V88より)

このへんの機構は、以下の動画がすごく勉強になりました。ありがとうございます。

www.youtube.com

でも、このへんをUnityの物理演算だけで実現しようとするとチューニングが大変なので、ここはC#で実現することにする。すなわち、ロックピンの状態として

  • Open (ロックピンはナックルの上に乗っておりナックルの動きを制限しない)
  • Close(ロックピンはナックルの横に落ちており動きを制限している)
  • ForcedOpen(ロックピンは端部突起に引っかかっておりナックルの動きを制限しない)

の3つを定義し、ロック解除時の状態遷移は下記とする。

  1. ロックピンを持ち上げる。(Open, Closeの判定は毎フレーム実施)
  2. ロックピンの状態がCloseからOpenとなったら、ForcedOpenに変更し、ロックピンの位置を固定する。
  3. ナックルがある程度開いたら、ForcedOpenからOpenに変更し、ロックピンの位置固定を解除する。

これでたぶん行ける。