2005/11/18

速度超過は御法度!  VRMスクリプト禅問答
【怎麼生】
速度制限区間を作りたいと思います。
その区間に入ると制限速度が表示されて、制限速度を無視したまま走りつづけると自動的に減速される、みたいな感じにしたいのですが、どうすれば良いですか。

【説破】 4.0.2.4
言葉で書くと1つのルール(=速度制限)のように見えますが、これをスクリプトで実現するには、複数の手順の組み合わせとして考える必要があります。
このような場合、「どのような処理」を「どのような順番」で実行しなければならないか、箇条書きにして整理してみることをお奨めします。たとえば、以下のように。

(1) センサーが編成を検知する。
(2) 制限速度が表示される。
(3) 少し待つ(運転手=ユーザーが減速するための余裕)。
(4) 制限速度が守られているか調べる。
(5) 超過している場合、制限速度まで減速する。



次に、それぞれについて、VRMスクリプトでそれを実現する方法を考えます。

(1)はセンサーイベントですね。つまり、SetEventSensor命令を使って編成が検知された際に実行されるメソッドを設定します。そのメソッドの中には、(2)制限速度を表示するためにDrawMessage命令が必要になります。ここまでで、とりあえず「その区間に入ると制限速度が表示され」が実現されますね。
(3)少し待つには、SetEventAfter命令で適当な時間が経った後に実行されるメソッドを設定することになります。これは、制限速度を表示した時点から、ということになりますから、先のDrawMessage命令が含まれるメソッドと同じところに書けばよいでしょう。
一方、SetEventAfter命令から実行されるメソッドは(4)制限速度が守られているか調べなければなりません。編成の走行速度を知るには編成スクリプト側でGetCurrentSpeed命令を使う必要がありますから、このメソッドは編成スクリプトに書くことにしましょう。すると、SetEventAfter命令は、センサーが検知した編成オブジェクトがどの編成かを知らなければならないことになりますから、合わせてGetSenseTrain命令も使うことになります。
(5)は、せっかくなので以前に例示した汎用速度設定メソッドMtdChangeSpeedを流用することにしましょう。

と、ここまでで、どのようなスクリプトを書かなければならないか、どんな命令を使うのか、が明らかになりました。これも整理するために改めて実行すべき処理を箇条書きにまとめてみましょう。合わせて、メソッドにも適当な名前をつけてやることにします。

[1] センサースクリプトで編成検知時に実行されるメソッドMtdSenseTrainをSetEventSensor命令を使って設定する。
[2] メソッドMtdSenseTrainの中でDrawMessage命令を使い、制限速度を表示する。
[3] 同じくメソッドMtdSenseTrainの中で(i)GetSenseTrain命令で検知した編成名を取得し、(ii)その編成の速度制限メソッドMtdLimitedSpeedが一定時間後に実行されるよう、SetEvnetAfter命令で設定する。
[4] メソッドMtdLimitedSpeedの中で(i)GetCurrentSpeed命令で現在の速度を取得し、(ii)if系命令で制限速度が守られているか調べる
[5] 制限速度超過の場合、メソッドMtdChangeSpeedをcallして減速する。

最初の「〜したい」に比べて、かなりプログラムっぽくなってきましたね。では、これを実際にスクリプト化したサンプルをご覧いただきましょう。
[センサースクリプト]
Var ConLimitedSpeed
setf ConLimitedSpeed 70.0 // 制限速度(調整可)
Var EventID
SetEventSensor MtdSenseTrain EventID // [1]
//編成検知メソッド
BeginFunc MtdSenseTrain
Var TimerID
VarTrain ObjTrain
DrawMessage "制限:70Km/h" // [2] 制限速度に合わせて要修正
GetSenseTrain ObjTrain // [3]の(i)
mov ObjTrain.VarLimitedSpeed ConLimitedSpeed // ※1
SetEventAfter ObjTrain MtdLimitedSpeed TimerID 3000 //[3]の(ii) 3000ミリ秒は調整可
EndFunc

[編成スクリプト]

Var VarLimitedSpeed // ※2
//以下は汎用速度制御メソッド用の変数
Var VarVelocityUp
Var VarVelocityDown
Var VarTargetSpeed
setf VarVelocityUp 5.0 // 加速度(調整可)
setf VarVelocityDown 5.0 // 減速度(調整上)
//
//制限速度メソッド
//
BeginFunc MtdLimitedSpeed
Var Tmp
GetCurrentSpeed Tmp // [4]の(i)
if>= Tmp VarLimitedSpeed // [4]の(ii)
mov VarTargetSpeed VarLimitedSpeed
call this MtdChangeSpeed // [5]
endif
EndFunc
//
//加減速メソッド(解説はこちら
//
BeginFunc MtdChangeSpeed
Var TmpSpeed
Var TmpVoltage
Var TmpTime
//目標電圧算出
mov TmpVoltage VarTargetSpeed
GetTopSpeed TmpSpeed
cnvfloat TmpVoltage
cnvfloat TmpSpeed
div TmpVoltage TmpSpeed
//時間算出
mov TmpTime VarTargetSpeed
GetCurrentSpeed TmpSpeed
cnvfloat TmpTime
cnvfloat TmpSpeed
if>= TmpTime TmpSpeed
sub TmpTime TmpSpeed
div TmpTime VarVelocityUp
else
sub TmpSpeed TmpTime
mov TmpTime TmpSpeed
div TmpTime VarVelocityDown
endif
mul TmpTime 1000.0
cnvint TmpTime
//加減速度実施
SetTimerVoltage TmpVoltage TmpTime
EndFunc
肝となる命令が実行される行のコメント(//以降)に、先に箇条書きに整理した実行すべき処理の番号を添えてみました。その対応関係を見比べてみてください。事前に何をすべきか、どのような順番で実行すべきか、が整理さえされていれば、スクリプトが魔法でも何でもないことがおわかりいただけるのではないか、と思います。
必ずしも妥当な比喩ではありませんが、日本語文を英訳するようなものだと思ってもらえば良いのではないでしょうか。必要なのは辞書(リファレンス)と、落ち着いて1つずつやるべきことを整理する論理的な思考です。

ちなみに、上掲サンプルコードは少し色気を出して、センサー側のグローバル変数ConLimitedSpeedにsetf命令で代入する数値を変更すれば任意の制限速度が設定できるようにしてみました。つまり、編成スクリプトにはまったく手を入れなくても、レイアウト内の速度制限区間にセンサーのコピーを配置し、それぞれの変数ConLimitedSpeedの値だけ修正すれば(実際には[2]のDrawMessage命令が表示する速度も、ですが)自由に制限速度を設けることが出来ます。この制限速度の編成オブジェクトへの受け渡しをおこなっているのが※1と※2で示した部分になるのは、本連載に親しんでおられる方にはもうご理解いただけていますね。
少し手を加えれば、制限速度解除時の再加速の自動化もできると思います。これは読者の皆さんの課題としてお楽しみいただくことにしましょう。
0

コメントを書く

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




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