keimei main -> キハポスト@VRM -> VRM4スクリプト -> スクリプト〜グローバル変数編

VRM4スクリプト(7)〜グローバル変数編

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

●メソッドの名前参照●

メソッドはBASICでいうサブルーチンやC言語の関数だと思っていいと思いますが(だめ?)、決定的な違いがあります。それがオブジェクト参照っていうやつです。例えば、列車を停止させる TrainStopというメソッド(中身はこの際なんでもいいとして)があった場合、BASICだと、

GOSUB TRAINSTOP

とかやる訳ですね。しかし、この場合、レイアウトに列車が何本もあったとするとどうでしょう? 「TrainStopっていうたかって、どこの誰に言うとるんや?」ということになります。そこで、この「どこの誰」も指定してあげる必要があります。

call "E751" TrainStop

"E751"という名前が付いた列車に対して、TrainStopしろという訳です。E751が何本もあるなら、編成エディタで、"E751A"とか"E751B"とか名前を変えてあげればよろしい。本物のように、"8086M"とかでもいいですね。この辺は車輌制御回天編で述べた通りです。

では、相手の名前が分からないと命令できないのかというと、そうでもありません。例えば、センサ上を列車が通過した場合、センサとしては、『どこの誰か知らんが、わしを踏みつけて行ったやつ、ちょっと止れや!』と言いたいですね。それを上品な言葉で書くと、

VarTrain T1
GetSenceTrain T1
call T1 TrainStop

「GetSenceTrain T1」が「わしを踏みつけて行ったやつ」で、「call T1 TrainStop」が、「ちょっと止れや」です。そして、「どこの誰か知らんが」が「VarTrain T1」となり、編成名を直接指定する代わりに、Train変数を用いています。

この様に、名前で誰に対する命令かが分かりますので、TrainStopという名前は複数のスクリプトにあっても構いません。逆に、同じ機能を持つメソッドは同じ名前にしておかないと、せっかくのオブジェクト指向のメリットが活かせずややこしい事態になってしまいます。

話が前後しますが、スクリプト自身も「誰の物か?」ということが重要です。例えば、列車の速度を制御するのに「SetVoltage」という命令を用いますが、これは「編成」に属する命令です。従って、編成のスクリプトに記述しないと機能しません。スクリプトマニュアルの「オブジェクトの種類」に書いてあるのが、命令が属すべきオブジェクトの一覧ですが、一覧が書いてあるだけで何も解説がないですね。各命令はこの一覧のどれか1つに属するので、その点を明記して欲しかったです。

●変数の名前参照●

変数の場合も「誰の物か?」という属性があります。例えば、編成のスクリプトの BeginFunc〜EndFuncの外側で、

Var T1

と宣言すると、それは「編成スクリプトの持ち物」ということになります。これを「オブジェクトのプロパティ」と言いますが、ちょっとピンと来ない用語ですよね。こういう用語が直感的に理解できる人は、スクリプトなんてお茶の子さいさいって訳なんでしょう。なお、プロパティとして外部に公開するのには、BeginFunc〜EndFuncの外側で宣言しないといけないという条件があります。

さて、メソッドと同じように、名前を指定してあげることによって、他人の変数を使うことが出来るようになります。例えば、上記の編成スクリプトで定義した「T1」という変数をレイアウトスクリプトで用いたい場合、以下のようにします。

Var M1
mov    this   M1  "E751"  T1
代入せよ ここの M1に E751の T1を

赤で命令に該当する日本語を書いてみました。プログラムの命令は後ろから読んだ方が分かりやすいですね。こうやって日本語にすると実は至極簡単なのです。なお、「this」は自分自身が持っているという意味の特別な記述です。

●応用〜ATCの元●

解説ばかりでつまらないので、応用編をやってみます。

注:ATC制御は、KZさんもやるって宣言されていますが、これは偶然で真似した訳じゃありません(^^; 第0号には地上信号機が含まれていないので閉塞運転ができないなぁと思っていたのですが、信号機がないから閉塞運転が出来ない→信号機なしで閉塞運転するには?→ATCだ!っていう逆転の発想でした。

レイアウトとして単純なオーバルループを用意します。そこへ「T253」という編成を1つ設置します。「T253」は、このレイアウトをぐるぐる回るだけですが、手始めにこの列車の現在位置を検出することを考えます。

今回はトップダウンで、レイアウトスクリプトから書いて行きましょう(レイアウト | スクリプト編集) 。

//ATC Control
//Layout script
Var count
Var ID1
Var delt
Var s

set delt 2000
SetEventTimer this atc ID1 delt

BeginFunc atc
  DrawMessage "This is control. Now position ="
  DrawVar count
  call "T253" SpeedGet
  mov this s "T253" NowSpeed
  DrawMessage " Now Speed ="
  DrawVar s
EndFunc

「count」というのが現在位置を示す変数です。閉塞区間を数字で表すことにして、「count」が「3」だったら列車は第3閉塞にいるということです。レイアウトのスクリプトは何も制御していません。「atc」というメソッドで「count」と、ついでに列車のスピード(s)を表示しているだけです(ビューアー左下のウィンドウに表示されます)。atcメソッドは、SetEventTimerによって2秒ごとに起動され、表示が更新され続けます。

//編成
Var NowSpeed

BeginFunc SpeedGet
  GetCurrentSpeed NowSpeed
EndFunc

列車のスピードを表示するなんて機能を持たせてしまったので、編成スクリプトに現在の速度を検出するメソッドをちょこっと書いておきます。スピードは「NowSpeed」という変数に代入されますが、これは編成スクリプトの持ち物です。従って、レイアウトスクリプトでこれを参照する場合、"T253"という名前を指定しています。

で、最後に編成の現在位置を検出するためにセンサを設置します。ここでは、ループ上に均等に5つのセンサを置いてみました。

//センサ(例:3番目)
Var sID3
VarLayout L00
Var p

set p 3
getlayout L00

SetEventSensor sens sID3

BeginFunc sens
  mov L00 count this p
EndFunc

5つのセンサにそれぞれスクリプトを書きます。上記は3番目のセンサの例です。列車がどこに居るか?→最後に何番目のセンサが検出したか?ということが分かればいいですね。変数「p」が何番目のセンサかを表していて、3番目のセンサの場合は、「p」に「3」をセットしています。そして、この「p」をレイアウトスクリプトの「count」変数に代入すればいいのです。ここで1つ注目は、レイアウト自身も名前を持っていることです。その名前は具体的には明示されていませんが、「getlayout L00」でゲットすることが出来ます。L00に何が代入されているのか興味がある所ですが、誰も知らない知られちゃいない‥‥‥ってことはないですが。ともあれ、L00という変数で、レイアウトスクリプトの「count」変数にアクセスできます。

レイアウトスクリプト

Var count

自分自身の持ち物なので「this count」でアクセスできる

センサ3

レイアウトが持っている countにアクセスするには
getlauout L00して、「L00 count」とする

これで、完成です。列車を走らせてみましょう。左下のログウィンドウは開いておいて下さいね。そこにpositionとspeedが表示されていると思います。speedはさらに左下にも表示される訳ですが、こっちの方が小数点以下の桁数が多いですね。そして、positionが刻々と変化していきます。例えば、positionが「3」の場合、列車はセンサ3とセンサ4の間の閉塞区間にいるということになります。

取り敢えず、ここまでです。これを応用・発展させていくと、なんとなくATC運転が出来そうな気がしますので、後日、頑張って作ってみます。今回使ったレイアウトは以下からダウンロードできます。例によって保証はなしです。

ATC実験用レイアウト(ATC_LOOP.LZH:363kB)