2005/7/29

どちらから列車が来たか知りたい  VRMスクリプト禅問答
【怎麼生】
センサーで列車を検知する際、どちらの方向から来たかに応じて異なるアクションを起こしたいのですが、列車が進入して来た方向を調べる方法がわかりません。

【説破】 4.0.1.2
VRM4第2号あたりで、信号機との兼ね合いからGetSenseDirectionとか、そんな感じの命令が追加されて、センサー部品に矢印マークでも添えられそうな気がするので、それまで待ってください。

まぁ、そういう冗談はさておき。
編成の向きが線路に対して不変なのであれば、GetSenseTrainした編成オブジェクトからGetDirectionすれば済む話ですが、この方法には(1)編成の配置時の向きに気をつかうのが面倒、(2)編成側に外部参照にそなえてグローバル変数にGetDirectionの値を書き出すメソッドが必要になる、(3)リバース線を含む、つまり同じ経路を向きを転じた編成が走行するレイアウトでは使えない、という欠点があります。
そこで、より汎用的に使えて、かつ、直感的なサンプルを示しておくことにしましょう。この方法では同じ経路上に配置したセンサーを2つ使います。
[センサースクリプト]

//パートナーセンサー定義
VarSensor ObjPartnerSensor
get ObjPartnerSensor "{パートナーとなるセンサーのオブジェクト名}"
//編成通過フラグ
Var StdVarTrainPass
set StdVarTrainPass 0
//イベント定義
Var EventID
SetEventSensor StdMtdTrainPass EventID

//
//編成通過検知メソッド
//
BeginFunc StdMtdTrainPass
//パートナーセンサーの状態を確認
mov this StdVarTrainPass ObjPartnerSensor StdVarTrainPass
ifzero StdVarTrainPass
//パートナーの値が0、すなわち編成はこちらから進入した
//ここに実行したいアクションを記述
//
//例:検知した編成のSTOPメソッドを実行する
// Var ObjTrain
// GetSenseTrain ObjTrain
// call ObjTrain STOP
//
//パートナーに編成の通過を知らせる
set StdVarTrainPass 1
else
//パートナーの値が0でない、すなわち編成はパートナー側から進入した
//次の編成の通過に備えてStdVarTrainPassをリセットする
set StdVarTrainPass 0
endif
//パートナーのStdVarTrainPassをリセットする
set ObjPartnerSensor StdVarTrainPass 0
EndFunc
「センサーを2つ使う、と言いながら、スクリプトが1つとはこれ如何に?」とお思いかも知れませんが、これでいいんです。2つのセンサー、たとえばSENSOR1とSENSOR2のそれぞれに、基本的には同じスクリプトを書きます。再利用可能なコード、ってヤツです。とは言え、まったく同じでは意味がありません。2つのセンサーのスクリプトで異なるのは以下の2点です。
1つ目は、冒頭のパートナーセンサーの定義です。具体的にはget ObjPartnerSensorで取得するオブジェクト名です。おわかりと思いますが、SENSOR1のスクリプトでは"SENSOR2"を、逆にSENSOR2のスクリプトでは"SENSOR1"をgetします。センサーの相思相愛、まさしくパートナーの誕生であります。
2つ目は、メソッドStdMtdTrainPassの中の「こちら側から編成が進入した際に実行したいアクションを記述」としている部分です。ここに、その名の通りのこと、つまり、通過した編成オブジェクトの何某かのメソッドをcallするとか、まぁ、そういうことを書きます。

では、このスクリプトがどういう理屈で動作するのかを追ってみましょう。ミソとなるのは、2つのセンサーそれぞれのグローバル変数(プロパティ)StdVarTrainPassの遷移です。
まず、SENSOR1側から列車が来たと考えてください。SENSOR1を編成が踏むとメソッドStdVarTrainPassが実行されます。ここでメソッドは、おもむろにパートナー側(つまりSENSOR2)のStdVarTrainPassを調べます。後の説明を読めばわかりますが、この変数が1であることは、すなわち既にSENSOR2が踏まれていることを意味します。逆に0であれば、SENSOR2が踏まれていない、つまり編成はSENSOR2方向から来たのではないことがわかります。
そこで続くifzero StdVarTrainPassで、この値が0かどうかを判断します。elseまでのスクリプトはStdVarTrainPassが0の場合、つまり編成がSENSOR1側から進入した場合に実行されることになりますから、ここにその場合に必要な処理(編成やポイントのメソッドcall)を書きます。そして最後に、SENSOR1のStdVarTrainPassに1をセットします。これでSENSOR1側の処理は終わりです。
列車は続いてSENSOR2を踏むはずです。SENSOR2でもメソッドStdVarTrainPassが実行されますが、パートナー側(つまりSENSOR1)のStdVarTrainPassはさきほどとは異なり1になっています。ifzero StdVarTrainPassは条件を満たさないのでelse以下のスクリプトが実行されます。
ここでは自分自身(SENSOR2)とパートナー(SENSOR1)それぞれのStdVarTrainPassに0をセットします。これは、次に他の編成が通過した場合にも正しく動作するための処理です。つまり、2つのセンサーの状態を元に戻しているわけです。

SENSOR2側から列車が来た場合は、まったく逆のことが起こります。つまり、SENSOR2のifzero StdVarTrainPass以降に記述されたスクリプトが実行されます。要するに、SENSOR1側から列車が来たときの処理はSENSOR1に、SENSOR2側から列車が来たときの
処理はSENSOR2に書けばいいということです。これが「直感的」の所以であり、また、ほとんど同じことが書かれたスクリプトにも関わらずこういう動作を実現できるところが、オブジェクト指向的スクリプティングの面白さである、とも言えるでしょう。

ちなみに。
センサーはレール部品1つにつき1つずつ、という制約があるようです。この方法で編成の進行方向に応じたアクションをおこしたい場合は、2つのセンサーは異なるレールに配置する必要がある点に注意してください。必ずしも隣接したレールでなくても構わないので、極端な話、単線区間の閉塞なんかにも応用できそうな気がします。それについてはまた気が向いたときにでも。
0

2005/8/20  18:05

投稿者:ghost
補足:どうもGetDirection命令の仕様を誤解していたようで、本稿のような面倒をしなくても、レールの向き(部品の回転原点の位置と編成進入方向の関係)を厳密に管理できる限りにおいて、センサーを通過した編成の方向を1つのセンサーで知ることは可能です。
が、現実にはレイアウター上で直感的に配置レールの向きを把握するのは困難であり、センサー位置によってどちらからくる編成にアクションを仕掛けるのか明確であるという点において、本稿で紹介している方法は有効とも言えます。
下記URLも合わせて参照あれかし。

http://silver.ap.teacup.com/ghost/196.html

コメントを書く

この記事にはコメントを投稿できません




teacup.ブログ “AutoPage”
AutoPage最新お知らせ