今回より五月雨式に,VRMNXpyでいろんなことをしてみる不定期連載をしていきます。
文体は簡便のため「だ・である調」となります。Python一般のごく基本的なところは他の文献・サイト・ブログをあたるのがよいでしょう。いいものがたくさんあります。
質問・文句・ツッコミ・その他,常に歓迎です。コメント欄にお願いします。「こんなことがしたい」といったリクエストもどうぞ。
<注意> VRM NXは現在βテストの途上であり,開発の経過に伴ってあらゆる仕様は変更されうることが宣言されている。本エントリに示す情報は執筆時点でのものであり,暫定的なものである可能性がある。 (2019/1/8 現在 Ver. 6.0.0.20)
【今回のお題】Ver. 6.0.0.20βでは,自動センサーの「指定距離停止」のコマンドや,編成のAutoSpeedCTRLメソッドが完全に機能しないようなので,その機能をVRMNXpyで組んでみよう。
センサーイベントの取り扱い方で,イベントハンドラの基本的な性質が分かっていただければ幸いである。
▼レイアウターツール▼ツールオプション▼プロパティ▼スクリプト から,「スクリプトを開く」と,センサーのスクリプトが開ける。システムが自動で書いたイベントハンドラがあるはずだから,それを書き換えて,「センサーを列車が通過すると,stop_in_distance関数を呼び出して400ミリで停止させる」ようにしよう。センサーのイベントハンドラは次のようになる。vrmevent_38
の数字の部分が大事なので,システムが自動で振った番号を書き換えないように注意。(以下のサンプルコードではたまたま38になっているだけ。)
いわゆるセンサーイベントをわざわざ設定しなくても,編成がセンサーを通過すれば勝手にイベントハンドラが呼び出されてくる仕様だから,SetEventATSのような命令は不要である。
def vrmevent_38(obj,ev,param): if ev == 'init': dummy = 1 elif ev == 'broadcast': dummy = 1 elif ev == 'timer': dummy = 1 elif ev == 'time': dummy = 1 elif ev == 'after': dummy = 1 elif ev == 'frame': dummy = 1 elif ev == 'catch': if param['dir'] == 1: # param['train'].AutoSpeedCTRL(200.0, 0.0) #コメントアウト# stop_in_distance(param['train'], 400)
イベントハンドラは,呼び出されたときにobj, ev, paramの引数を伴ってくる。objはセンサー自身である。(今回使ってないけど。)evは発生したイベントの種類が何であるかが文字列で入っている。(文字列なのはやや気持ち悪いが・・・)センサーを列車が通過したときは'catch'だそうだ。
イベントの種類に応じていろいろな情報が辞書型のparamでついてくる。センサーのcatchイベントであれば,trainid, train, dir, tireの4種類の情報だとマニュアルに書いてある。trainid, train, dir, tireなどを辞書のキーといい,中身はparam['train']
のように参照できる。イベントの種類に応じてparamに入っているものが異なるので,存在しないキーを参照しないよう,コーディングの際は十分注意が必要だ。
肝心のstop_in_distanceはこう書けばよい。以前調べたように,編成でもセンサーでもどこに書いてもよい。分かりやすいのが一番だろうが・・・
def stop_in_distance(trn, dist): # 指定距離で停止 (AutoSpeedCTRLと一緒) # 引数 # trn 対象の編成オブジェクト # dist 停止距離[mm] # 返り値 停止時間[秒] spd = trn.GetSpeed() #現在速度を取得 spd = spd/150*1000000/3600 #模型スケールのmm/秒に変換 stoptime = 2*dist/spd #減速時間 vrmapi.LOG('[SENSOR] 時間'+str(stoptime)) trn.SetTimerVoltage(stoptime, 0.0) return stoptime
速度から減速時間を求める計算がいかにも高級言語らしく書けるのが素晴らしいが,変数宣言なしに変数が使えちゃうのが気持ち悪いと思う人もいるかもしれない。どちらも(旧VRMスクリプトと違って)Pythonらしいところである。
確認用にログウィンドウに減速時間を書き出すようにもした。stoptimeの中身はfloat型であるがLOGに書き出すにあたって文字列に変換してつなぎ合わせるため,str関数(これは,Python標準の関数である)を噛ましている。
肝心の,編成に対するSetTimerVoltageは,trn.SetTimerVoltage(stoptime, 0.0)
のように書く。(旧VRMスクリプトと引数の順番が逆で時間の単位が秒になっている。)
「対象オブジェクト.命令」の形式で書くのは,旧VRMスクリプトに慣れ親しんだ人には気味が悪いかもしれないがPythonの仕様といえる。この記法を取ることになっているので,編成の特定の動作をまとめた関数は,必ずしも編成のスクリプトに書く必要はなく,またレイアウト全体でただ1回だけ書けば良いということにもなっている。旧VRMスクリプトで,いくつもの編成に何百行もの同じコードをコピペして回っていたのが嘘みたいだ!