踏切をセンサーで作動させてみる(複線の場合)

さて、前回では上り線のみの場合で考えましたが、以下のようなスクリプトで組めば問題なく複線の踏切を動作させられそうです。


目印として赤いコンテナが遮断機に「閉める」と命令するセンサー(以下、「閉める」センサー)、
青いコンテナが遮断機に「開ける」と命令するセンサー(以下、「開ける」センサー)とします。
なお、見やすくするため「閉める」センサーはかなり踏切に近づけて配置しています。

では、以下の状況を考えてみます。


まず、踏切のすぐ側に駅があったとします。これは実景にもよく見られます。
なお、左下から右上へ行く方を上り、その逆を下り線とします。


下り普通列車が駅に停車し、下り線の「閉める」センサーが作動して遮断機が閉まります


下り普通列車停車中に上り特急列車が駅を通過しようとしています。
このとき上りの「閉める」センサーを踏みましたが、元から閉まっているため遮断機は下りたままです。


上り特急列車が駅を通過中に上りの「開ける」センサーを踏みました。
このときに遮断機が上がってしまいました。


このときすぐに下り普通列車が出発しても下り線の「閉める」センサーは作動済みのため遮断機は開いたままです。

このほかにも高速貨物列車と単機回送のすれ違いなどにもこのような現象が現れてしまいます。

まぁ仮想の世界なので「その時はその時」で済む話なのですが、折角"スクリプト"なんてものがあるので実物と同じ動きにしてみます。

 

今回の場合何が問題なのか、それは「開ける」センサー1回の反応で「閉める」センサーが2回反応したのを帳消しにしてしまったところです。
ということは「閉める」センサーが反応した数を数えればいいわけです。
例えば"「閉める」センサーが反応したら+1"、"「開ける」センサーが反応したら−1"としてやって

一つ増えたとき→閉める
二つ増えたとき→閉める
元に戻ったとき→開ける

と決めておくと問題なくこの現象が解決されます。

あと必要なのは"「閉める」センサーが反応した数を数える人"です。
ただし、ビュワーの中に人はいないので部品(オブジェクト)に数えさせます。

となるとあるオブジェクトの中に数を数えるスクリプトをつくります。
ただ、何も関係ないオブジェクトにこのスクリプトを押しつけるのはなんともナンセンスなので警報機(TOMIXは遮断機と警報機が一体なので今まで書いていた遮断機)に書き込みます。

さて、書き込オブジェクトが決まったので早速作っていきます。
今回のスクリプトのイメージは

一つ増えたとき→閉める
二つ増えたとき→閉める
元に戻ったとき→開ける

でした。
これを少し言い換えて、まず"最初に適当な数字"を決めます。
そうすると

(最初に適当に決めた数字)→開ける
(最初に適当に決めた数字)+1→閉める
(最初に適当に決めた数字)+2→閉める

となります。
この"適当に決めた数字"を言い換えると"数字ならなんでもOK"なワケですね。

さて、ここで初回を思い出して下さい。
この"数字ならなんでもOK"というのは難しく言うと"変数"となるわけです。

つまり"適当に決めた数字"="変数"なのでもっと言い換えると、まず"最初に変数"を決めるということになります。
例えばaを変数とします。すると・・・

aの時→開ける
a+1の時→閉める
a+2の時→閉める

となります。ただこれではスクリプトを作るときなんとなく掴みにくいので最初にa=0とします。
すると・・・

0の時→開ける
1の時→閉める
2の時→閉める

なんとなく形が見えてきました。
今までのことをまとめると

1.最初に適当に変数を決める。
2.その変数を0とする。
3.変数が↓
0の時→開ける
1の時→閉める
2の時→閉める

となります。


ちょっと息抜き、飯田線

ではこれを元にスクリプトを組んでみます。

最初の

1.最初に適当に変数を決める。
2.その変数を0とする。

の部分は初回で言ったとおり

Var a
set a 0

でOKです。

問題なのは3.の
変数(上記スクリプトで言うと"a")が↓
0の時→開ける
1の時→閉める
2の時→閉める

という部分です。
ここでスクリプトマニュアルを色々見てみると、if文という欄にifという命令文があります。
まぁ色々書いてありますが、詰まるところコーディングサンプルにあるように

if a
//aが0以外の時ここが実行される
else
//aが0のときここが実行される
endif

//※一部略記

ということです。なんかこれは使えそうな命令文です。 ということで早速コーディングサンプルを元に作ってみると・・・

if a
//aが0以外の時ここが実行される
//ここで「閉める」スクリプトを発動させる
else
//aが0のときここが実行される
//ここで「開ける」スクリプトを発動させる
endif

ということは、1.2.も合わせてみると

Var a
set a 0

if a
//ここで「閉める」スクリプトを発動させる
else
//ここで「開ける」スクリプトを発動させる
endif

となります。

さっそく警報機を配置します。

んで、オブジェクト名もcross1-akとでもしておきます(最後のkは警報機のkね)
ついでに反対側の警報機もcros1-bkとします

前回でやったとおり、警報機に以下のスクリプトを書き込みます

BeginFunc close
SetCrossingStatus 2
EndFunc

BeginFunc open
SetCrossingStatus 1
EndFunc

ここまでのスクリプトの意味はOKですね

さて、前回書いたとおりBeginFuncより下の部分はビュワー起動時には発動されないのでこのスクリプトの上側に書き込んでいきます。

今回まとめたやつを上に載せると・・・

Var a
set a 0

if a
//ここで「閉める」スクリプトを発動させる
else
//ここで「開ける」スクリプトを発動させる
endif

 

BeginFunc close
SetCrossingStatus 2
EndFunc

BeginFunc open
SetCrossingStatus 1
EndFunc

となります。

しかしこれでは問題があります。
前回の通り、BeginFuncより上にあるものはビュワー起動時にしか発動しないため何回も発動させるためにはifもBeginFuncで固めなければなりません。
ということで、BeginFuncで固めます。メゾット名は適当に"kazoeru"とします。

Var a
set a 0

BeginFunc kazoeru
if a
//ここで「閉める」スクリプトを発動させる
else
//ここで「開ける」スクリプトを発動させる
endif
EndFunc

 

BeginFunc close
SetCrossingStatus 2
EndFunc

BeginFunc open
SetCrossingStatus 1
EndFunc

最初の2行は最初に設定すればあとは何回も設定し直す必要がないのでBeginFuncで固めなくても大丈夫ですね。

あとは "//ここで「閉める」スクリプトを発動させる""//ここで「開ける」スクリプトを発動させる" の部分だけです。
発動させるためにはcallという命令文を使います(前回参照)、ちなみに発動させるスクリプトが書かれているオブジェクト名は自分自身なのでthisを使うことが出来ます。 つまり

call this close

で自分自身(今スクリプトを書いている部品)のスクリプトに書かれているメゾットcloseを発動せよ。となるのです。

というわけで、これを踏まえて書くと・・・

Var a
set a 0

BeginFunc kazoeru
if a
call this close
else
call this open
endif
EndFunc

 

BeginFunc close
SetCrossingStatus 2
EndFunc

BeginFunc open
SetCrossingStatus 1
EndFunc

となります。
これで警報機のスクリプトの基盤は完成です。
あとは、遮断機や残りの警報機にも発動させるようにcallを追加します。
Var a
set a 0

BeginFunc kazoeru
if a

call this close
call cross1-a close
call cross1-b close
call cross1-bk close

else

call this open
call cross1-a open
call cross1-b open
call cross1-bk open

endif
EndFunc

 

BeginFunc close
SetCrossingStatus 2
EndFunc

BeginFunc open
SetCrossingStatus 1
EndFunc

もちろん、残りの警報機と遮断機には以下のスクリプトを書き込みます。
もし書き込まなかった場合「この部品にそんなメゾット(機能)無ぇ!」とビュワー起動時に怒られます。

BeginFunc close
SetCrossingStatus 2
EndFunc

BeginFunc open
SetCrossingStatus 1
EndFunc

さて、お次はセンサーです。

今回センサーに課せられるスクリプトは
"「閉める」センサーが反応したら+1"、"「開ける」センサーが反応したら−1"
ですね、今回は+1や−1する数は"a"という変数なのでもっと具体的に書いてやると
"「閉める」センサーが反応したらa+1"、"「開ける」センサーが反応したらa−1"
となります。

 
まず、"「閉める」センサーが反応したらa+1"の部分から作っていきます。

まず、+1の部分ですが、これはスクリプトマニュアルの演算という欄にaddという命令文があります。
ここで書かれていることは
add 変数A 変数B

変数A = 変数A + 変数B

変数Bは、変数の代わりに数値を指定可能です。

ということはつまり
add a 1

でa+1となります。

というわけで早速「閉める」センサーにこの構文を書き込みます。


書き込む場所はsnsevの中ですね。外に書いた場合1度しか発動されません。

これでビュワーを起動すると・・・

はじき返されてしまいます・・・

「閉める」センサーのスクリプトをよく見てみると、「"a"は変数だよ」と宣言しておりません。
しかし「閉める」センサー内で「Var a」と書いてしまうと警報機にある変数aとは全くの別物になってしまいます。

上手いこと警報機で宣言した変数aに1を足す方法を考えなくてはなりません。

ここで使うのが非常に便利なobject.variable形式です。これはスクリプトマニュアルの変数操作の欄のmovという所の最後にちょこっと載っています。

つまり早い話が部品名と変数名の間にドットを挟む形、すなわち「部品名.変数名」で「部品の変数」にアクセスできるのです。
具体的に書くと

add cross1-ak.a 1
※ドット部分を見やすくするため文字を大きくしています。

でオブジェクトcross1-akで変数であると宣言されたaに1を足すという意味になります。

しかしBeginFunc-EndFuncの間に宣言した変数はこのようなことができません。
このようなBeginFunc-EndFuncの間に宣言した変数のことをローカル変数と呼び、ローカル(特定の場所に限られたもの)な変数なのでこのように外部から1を足したり引いたりといったことはできません。
必ず、BeginFunc-EndFuncの外側で宣言して下さい。
ちなみにBeginFunc-EndFuncの外側で宣言した変数をグローバル変数と呼び、グローバル(全体的な)変数なので1を足したり引いたり色々なことができます。


というわけでこれを書き込んで・・・

これで起動すると・・・


ちゃんとビュワーが起動しました!

しかし肝心の警報機が作動していません。
これはどこにも警報機のメゾットkazoeruを発動させるcallが無いからです。

というわけで書きます。
しかし、警報機のメゾットkazoeruの中でcross1-aのcloseやcross1-bのcloseを発動せよと命令しているのでここでこのcallを書くのは不必要です。

つまり・・・

としてやれば、あとは警報機のメゾットkazoeruがなんとかしてくれます。

しかし、このように

「add cross1-ak.a 1」と「call cross1-ak kazoeru」を逆にした場合、+1をする前にkazoeruメゾットが起動してしまうため全く意味のないスクリプトのなってしまいます。

さて、これで一度ビュワーを起動してみます。

ちゃんと警報機が作動しました!

ここで一度スクリプトの流れをまとめます。

1.「閉める」センサーが反応
2.cross1-akにある変数aに+1する
3.cross1-akのメゾットkazoeruを発動する

ここまでセンサー、ここから警報機cross1-ak内部のスクリプト

4.センサーに変数aを+1される
5.メゾットkazoeruを発動
6.aを調べたところ、1だったので
call this close
call cross1-a close
call cross1-b close
call cross1-bk close
を発動。

ということはこの状態で−1してやったあとにkazoeruを発動させれば

6.aを調べたところ、1だったので
call this close
call cross1-a close
call cross1-b close
call cross1-bk close

であったのが、

6.aを調べたところ、0だったので
call this open
call cross1-a open
call cross1-b open
call cross1-bk open

という流れになって遮断機が上がります。

もちろん最初に示した場合のようにもし「閉める」センサーが2回発動した場合、aは2になるので、

6.aを調べたところ、2だったので
call this close
call cross1-a close
call cross1-b close
call cross1-bk close

ここで「開ける」センサーが1回反応しても、aは1となるので

6.aを調べたところ、1だったので
call this close
call cross1-a close
call cross1-b close
call cross1-bk close

となり、遮断機は閉まったままになります。

それでは今度は「開ける」センサーを作っていきましょう。

作ると言っても「閉める」センサーと違うところは+1するか−1するかの違いだけです。
「閉める」センサーと同様に演算のsubという所を見て下さい。
addと全く同じ形です。
というわけで全く同じように書くと・・・

Var evid
SetEventSensor snsev, evid

BeginFunc snsev
sub cross1-ak.a 1
call cross1-ak kazoeru
EndFunc

というようにaddがsubに変わっただけですね。
これで+1の部分が−1になったわけです

このスクリプトを上下線それぞれ2個ずつの「閉める」センサーと「開ける」センサーに書き込みます。

そしてビュワーを起動しますと・・・・

ちゃんと最初に示したような状況でも遮断機が上がることはありません。

これですべて完成です!お疲れ様でした。

次回は解説のしやすさを優先させて列車がセンサーを通過したら警笛を鳴らしてみるスクリプトの作り方を解説します。
単線の場合の踏切の動作はその後になります

次回へ>>>>

追記:
今回のスクリプトでは遮断機が上がりきる前に「閉める」スクリプトが発動した場合不自然な動作をします。
この解決法は後ほど解説します。


前へ戻る

inserted by FC2 system