前回のエントリはVRM NXに搭載された「Pythonベースのスクリプト」(長ったらしいのでVRMNXpyと呼ぶ)のマニュアルが全く未整備であったので,表面的な部分から実装がこんなのだったらいいなーと思って書いた。
まもなくして,思ったよりも早く,スクリプトマニュアルが現れたので,前回のエントリはたちまちGOMIになってしまったわけだが,想像していたのとだいぶ違う実装になっていたので書き留めておく。
<注意: VRM NXは開発途上の段階であり,βテストの経過に伴ってあらゆる仕様は変更されうることが宣言されている。本エントリに示す情報は執筆時点でのものであり,暫定的なものである可能性がある。>
まずは,どれでもいいから(編成Aとしよう)編成スクリプトの末尾に次のようなコードを足そう。ただし,インデントはPythonの仕様上半角スペース4つでなければならない。(ほんとうはTABでもいいはずだけどダメだった)
#編成スクリプト
def trnlog():
vrmapi.LOG('TRAINやで!CHOO-CHOO')
(決してカルボキシル基が2つくっついているのではない)
そして,レイアウトスクリプトからいまのメソッドを呼び出す。呼び出し方に注意。(このコードも末尾に追加してよい。)
#レイアウトスクリプト
trnlog()
VRM4~5のログウィンドウに当たるものは,下図に示したボタンからメニューを開くと参照できる。きちんと(?) LOGが出ていることがわかる。
(画像と記事本文でLOGの内容が異なるのは許容してほしい。)
この実行結果は非常に示唆的である。
バージョン4~5のVRMスクリプトでは,オブジェクトごとに名前空間が区切られており,編成Aで定義したMtdHogehoge
と,編成Bで定義したMtdHogehoge
は別物であり,参照時もオブジェクト参照とともにcall Obj MtdHogehoge
としなければならなかった。
しかし,VRMNXpyはそうではなく,全体で名前空間は唯一つのようである。(だからこそ,レイアウトスクリプトから編成Aの関数が直で呼び出せている。)さらに,前バージョンと抜本的に異なることとして,編成Aと編成Bで同じ名前の関数を書いてしまうと,pythonの仕様上上書きされるので,どちらか一方の関数(の中身)しか生き残らないことに注意しなくてはならない。(追試してみて欲しい)
これを回避するためのひとつの方法として,次のような書き方を思いつくことができる。イメージのしやすさのため,旧バージョンのコードと書き並べてみよう。
// 編成スクリプト (旧VRMスクリプト)
BeginFunc MtdStop
// センサーから call ObjTrain MtdStop とでも呼び出されるつもり
SetTimerVoltage 0.0 1000 //1秒かけて停止する
EndFunc
# 編成スクリプト (VRMNXpy)
import vrmapi
def trainstop(obj):
obj.SetTimerVoltage(1.0, 0.0)
#センサーからは次のように呼び出す
def vrmevent_99(obj, ev, param):
if ev ='catch':
trn = obj.GetTrain()
trainstop(trn)
(ただし,v6.0.0.9βでは自動センサーのイベントはうまく発生しないみたいだ・・・VRMATS.SetEventATS(self, ...)
のような関数はないみたいだが,なにかしなくちゃいけないのかな?)
(プログラミングは本職ではないのであまり詳しくないが,)Pythonにしてはずいぶんと関数型っぽいプログラムになっているような気がする。
なおこのことは関数だけでなく変数等でも同じことが起きるので,旧スクリプトでのグローバル変数も取り扱いが変わる。(こちらは,各オブジェクトが固有に持つべき公開プロパティはステータス領域に置きobj.Set/GetStatusXxxxでアクセスするのが妥当な実装であると思われる。)
ただし,akg/2.0のような自動運転システムを考慮するとき,たくさんの編成に同じメソッドをコピペして回らないでも,全体で1箇所(例えば,レイアウトスクリプト)に1回だけ書いておけばすべての編成から同じように呼び出せるようになる点にも注目する必要がある。
イベントハンドラの仕様自体,VRM遊びにフィットしているかどうかまだ判断がつかない(と思っているが,内心,ネガティブに捉えている)ところなので,今後のβテストの動向を注視したい。
ほかにうまい書き方を思いついた方は,コメント欄へぜひどうぞ。あらゆる質問・文句・ツッコミ・その他も歓迎します。