keimei main -> キハポスト@VRM -> VRM4スクリプト -> スクリプト〜車輌制御編

VRM4スクリプト(5)〜車輌制御編

この文章を書いた時点でのVRM4のバージョンは「4.0.0.4」です。これ以前のバージョンではうまく動作しません。また、今後のバージョンアップによって仕様が変更される可能性もあります。

●ウォーミングアップ●

車両に関する制御ですが、手始めにVRM3にあった「キーボードで編成を直接選択する」をやっておきます。既に誰かが作っていると思いますが、ここは自分流で。

例によって、原因と結果を分け、編成(結果)側は汎用性があるコードにします

//Train Side
VarTrain ac
BeginFunc active
  get ac "TE1MAX"
  SetActiveTrain ac
EndFunc

編成エディターの「スクリプトエディター」に書きます。「TE1MAX」というのは自分自身の名前ですので、「SetActiveTrain this」とか出来ると思ったのですが、ちょっと甘かったようです。レイアウトに設置した編成ごとに、それぞれ名前を変えてスクリプトを書きます。ここでは、「TE751」「TE253」「TE1MAX」の3編成あるとします。

//Main
Var cID1
Var cID2
Var cID3
SetEventKey this Tactive1 cID1 1
SetEventKey this Tactive2 cID2 2
SetEventKey this Tactive3 cID2 3

BeginFunc Tactive1
  call "TE751" active
EndFunc
BeginFunc Tactive2
  call "TE253" active
EndFunc
BeginFunc Tactive3
  call "TE1MAX" active
EndFunc

メニューのスクリプトエディター(メインと呼ぶことにします)に上記を書きます。ここでは、キーボードの「1」「2」「3」で、編成を選択できるようにしました。重ねて言いますが、原因と結果を分けて書くことが重要です。例えば、「1」「2」「3」キーは他で使っているので「a」「b」「c」にしたい、という場合も、このメイン側をまとめて書き換えるだけで済みます。また、SetEventKeyはメイン側にまとめて書いた方が、何のキーを既に使っているのか一目瞭然って訳です。

●光りあれ●

それでは、本番です。車両の室内灯やヘッドライトを制御してみましょう。「非制御編」で登場した SetRoomlightSetPantographなどを使いますが、車両によって、ヘッドライトのあるなし、パンタグラフのあるなしと様々ですので、車両に合わせて書く必要があります。ここでは、2つほど例を載せておきます。編成エディターの車両ごとに「右クリック→スクリプト編集」で入力していきます。

//先頭・最後尾車
BeginFunc roomON
  Var r
  set r 1
  SetRoomlight r
  SetSignlight r
EndFunc

BeginFunc roomOFF
  Var r
  set r 0
  SetRoomlight r
  SetSignlight r
EndFunc

BeginFunc HeadON
  Var r
  set r 1
  SetHeadlight r
  set r 0
  SetTaillight r 
EndFunc

BeginFunc TailON
  Var r
  set r 0
  SetHeadlight r
  set r 1
  SetTaillight r 
EndFunc

BeginFunc PantOFF
  Var r
  set r 0
  SetHeadlight r
  SetTaillight r 
  SetRoomlight r
  SetSignlight r
EndFunc
//中間・パンタ車
BeginFunc roomON
  Var r
  set r 1
  SetRoomlight r
  SetSignlight r
EndFunc

BeginFunc roomOFF
  Var r
  set r 0
  SetRoomlight r
  SetSignlight r
EndFunc

BeginFunc PantON
  Var r
  set r 1
  SetPantograph 0 r
EndFunc

BeginFunc PantOFF
  Var r
  set r 0
  SetPantograph 0 r
  SetPantograph 0 r
  SetRoomlight r
  SetSignlight r
EndFunc

ちょっと長いですが、もうこのくらいではビビらないですよね。基本的には同じような内容の繰り返しです。室内灯(SetRoomlight)と方向幕(SetSignlight)は、同時にオンオフしています。どちらか一方だけ点いているという場合は少ないと考えたからです。ヘッドライト(SetHeadlight)とテールライト(SetTaillight)も両方同時に点くことはないので、一方を点灯させたらもう一方はオフにするようにしています。さらに、パンタグラフ(SetPantograph)を下ろしたら電源が切れる訳ですから、室内灯などもオフにするようにしています。このため、パンタを持たない車両にも「PantOFF」を書いて、パンタ下降命令が来たら、照明をすべてオフにする処理を行っています。「PantOFF」は車両のすべての照明を切る命令と思って頂いた方が分かりやすいと思います。

注:上記は車両に関するブラックボックス的処理ですので、室内灯・方向幕・パンタグラフなどごとにオンオフするメソッドを作っておいて、室内灯と方向幕を同時に点けるという処理は、それを呼び出す側で処理した方がベターです。が、繁雑になると思ったので上記のような処理にしましたが、却って分かりにくくなったかもです。

注:ポイント制御でやったように、現在の状況をゲットして‥‥‥というのはやっていません。すでに、室内灯が点いているのに重ねてSetRoomlightでオンしても問題ないと考えたので割愛しました。実際、問題はないようです。

//main
Var sID1
Var sID2
Var sID3
VarCar c

Var Spant
Var Slight
Var Shead

SetEventKey this Mpant  sID1 1
SetEventKey this Mlight sID2 2
SetEventKey this Mhead  sID3 3

set Spant 1
set Slight 1
set Shead 0

BeginFunc Mpant
  if Spant
    set Spant 0
    set Slight 0
    getcar c "TE253" 0
    call c PantOFF
    getcar c "TE253" 1
    call c PantOFF
    getcar c "TE253" 2
    call c PantOFF
  else
    set Spant 1
    getcar c "TE253" 1
    call c PantON
  endif
EndFunc

BeginFunc Mlight
  if Slight
    set Slight 0
    getcar c "TE253" 0
    call c roomOFF
    getcar c "TE253" 1
    call c roomOFF
    getcar c "TE253" 2
    call c roomOFF
  else
    set Slight 1
    getcar c "TE253" 0
    call c roomON
    getcar c "TE253" 1
    call c roomON
    getcar c "TE253" 2
    call c roomON
  endif
EndFunc

BeginFunc Mhead
  if Shead
    set Shead 0
    getcar c "TE253" 0
    call c HeadON
    getcar c "TE253" 2
    call c TailON
  else
    set Shead 1
    getcar c "TE253" 0
    call c TailON
    getcar c "TE253" 2
    call c HeadON
  endif
EndFunc

メイン側はなんでもいいのですが、ここでは、1つのキーでオンオフを切替える、即ち、
1=パンタの上下
2=室内灯のオンオフ
3=ヘッドライト/テールライトの切替え
としました。もちろん、「1=パンタ上昇、2=パンタ下降」などとすることも出来ます。 1つのキーでオンオフを切替えますので、現在の状態を覚えておく必要があり、その役目を「Spant」「Slight「Shead」というグローバル変数(コモン・共通変数)に負わせました(安易な方法ですので、変数の内容と実際の状態が食い違うという危険性があります)。 せめてもの償いに、「set Spant 1」「set Slight 1」「set Shead 0」という初期化を行っています。例えば、パンタグラフ(Spant)は初期状態で開いていますので、「1」をセットしておきます(「BeginFunc〜EndFunc」の外側はビューアー起動時に一度だけ実行されますので、初期化ルーチンはそこに書きます)。‥‥‥やっぱり、分かりにくくなってしまいましたね。一応、これで編成全部の車両の室内灯を一括してオンオフする、などの操作ができます。

注:実はこのプログラムでは、パンタを下ろした状態でも室内灯が点いてしまいます。パンタを下ろした状態では「roomON」などは無視するという処理を加えないといけないですが、繁雑になるので割愛しました。というか、うまく書けなかったので、プログラムの得意な人、お願いします(^^;

●ちょっとお遊び●

なんか面白くないので、最後にちょっと変わったことをやってみます。

なんか横向いていますけど、上図は引き込み線のつもりで、ここに列車が進入したということは留置するってことですので、車止めにぶつかる前に「自動で停止し照明を全部オフにする」ことにします。また、いきなり停止するのは不自然ですので、ある程度減速してから停止することにします。

まず、編成に減速・停止するメソッドを書きます(「編成エディタ」→「スクリプトエディター」)。

//Train
BeginFunc T_teisi
  Var v
  set v 0
  SetVoltage v
EndFunc

BeginFunc T_gensoku
  Var f
  Var t
  setf f 0.3
  set t 1000
  SetTimerVoltage f t
EndFunc

BeginFunc T_turn
  Var v
  GetCurrentSpeed v
  ifzero v
    Turn
  endif
EndFunc

BeginFunc T_horn
  PlayHorn
EndFunc

ついでに方向転換するメソッドと警笛を鳴らすメソッドを書いておきました。こうやってなるべく汎用性のある処理を書いておくと、後々いろいろな用途に使えます。取り敢えず書いておいて結果的に使わなかったでも構いません。 なお、編成を停止するのは、SetVoltageで電圧を0にします(Nゲージの感覚ですね)。減速の場合は徐々に減速させるため、SetTimerVoltageを使っています。また、方向転換(Turn)は現在のスピードが"0"の場合のみ出来るとして、GetCurrentSpeedで現在速度を読み取っています。各命令の詳細はマニュアルを参照してください。

では、レール上の適切な場所にセンサを用意して、それに対するスクリプトを書きます。まず、退避線に入った辺りに減速用のセンサを置きます。

//Sensor Sub01
Var snsID1
VarTrain SnsTrain

SetEventSensor S_gensoku snsID1

BeginFunc S_gensoku
  GetSenseTrain SnsTrain
  call SnsTrain T_gensoku
EndFunc

これは、簡単ですね。センサで検出した編成の「T_gensoku」メソッドを呼び出しているだけです。センサが、自分が検出した列車に対して『お前、危ないから減速せなあかんぞ』と命令を出している訳です。

車止めの直前には、第2のセンサを置き、こっちはフルストップさせるスクリプトを書きます。

//Sensor Sub02
Var snsID2
VarTrain SnsTrain
VarCar c1

SetEventSensor S_teisi snsID2

BeginFunc S_teisi
  GetSenseTrain SnsTrain
  call SnsTrain T_teisi
  getcar c1 SnsTrain 0
  call c1 PantOFF
  getcar c1 SnsTrain 1
  call c1 PantOFF
  getcar c1 SnsTrain 2
  call c1 PantOFF
//  call SnsTrain T_turn
EndFunc

これも、センサが検出した編成に対していろいろ命令を出してします。「SnsTrain」に対して「T_teisi」命令を出したあと、今度は各車両に対して照明オフ(PantOFF)命令を出しています。これは、各車両に対する命令ですので、getcarで各車両をゲットしています。この場合、3両編成なので上記のようになりますが、対象となる列車の編成状況によって処理を変えないといけないので、ちょっと汎用性に欠けます(注)。

注:BASICでいう FOR〜NEXT命令があればいいのですけどね。あと、編成の車両数をgetする命令も必要かも。

注:照明を消した後、自動で方向転換するなら、プログラム下から2行目のコメント(//)を外せばOKです。また、退避線から出発することについては考慮していません。照明を手動で点けないといけないのはいいですが、出発後、減速センサーの上に達すると速度が勝手に最高速度の30%になってしまいます‥‥‥ま、それも愛敬ですが。センサをさらに追加して、列車がどっちから来たかを判定すれば、対処できないことはないですけど‥‥‥。

ここまで出来れば、もう何でも出来ます。例えば、
・ループを3周したあと、引き込み線に入って停止・照明を切る。
・駅で自動で停止・発車させる。
減速の方法も、例では、1秒かけて最高速度の30%(130km/hの場合39km/h)にしていますが、1秒というのはちょっと急過ぎるかもです。また、39km/h未満で進入した場合、加速することになってしまいます。この辺はいくらでも工夫できると思いますので、各人のコダワリを見せたいところですね。

なんでも出来ると書きましたが、「原因」となる事象が限定されています。第0号の段階では、
・キーボードのキーを押す(SetEventKey)
・センサが編成を検出する(SetEventSensor)
・一定時間後、または、一定時間周期でイベントを発生させる(SetEventTime、SetEventTimer)
しかありません。列車の速度が"0"より大きくなったら(列車が走り出したら)、ヘッドライトを点ける、なんてのは出来そうで(直接的には)出来ません。SetEventTimerで定期的に速度を監視する方法もありますが、処理が重くなりそうです。