【怎麼生】
レイアウトコンテスト等に応募するレイアウトで、背景として走りっぱなしにしたい編成があります。これをユーザー操作で選択出来ないようにしたいのですが、可能ですか?
【説破】 4.0.7.2
出来ます。基本的な考え方は以下の通りです。
(1) レイアウト上の編成を“選択可能な編成”と“選択禁止の編成”に分けます。
(2) “選択可能な編成”は、アクティブになった時点で自分がアクティブであることをレイアウト変数に記録します。
(3) “選択禁止の編成”は、自分がアクティブとなる操作がおこなわれた場合、(2)で記録した“直前にアクティブであった編成”をアクティブに切り替えます。
このような仕掛けをすることで、任意の編成を選択不可能な状態にすることが出来ます。ただし、この仕掛けの実装にあたっては、VRMスクリプトの仕様上の制約から、一工夫が必要です。
<レイアウトスクリプト>
VarTrain ObjActiveTrain //アクティブな編成を保存する変数
BeginFunc MtdChangeActiveTrain
Var tmp
SetEventAfter this MtdChangeActiveTrain2 tmp 1
EndFunc
BeginFunc MtdChangeActiveTrain2
SetActiveTrain ObjActiveTrain
EndFunc
<選択可能な編成の編成スクリプト>
Var EvtFocusIn
SetEventFocusIn MtdFocusIn1 EvtFocusIn
BeginFunc MtdFocusIn1
get LAYOUT.ObjActiveTrain this
EndFunc
<選択禁止の編成の編成スクリプト>
VarLayout ObjLayout
getlayout ObjLayout
Var EvtFocusIn
SetEventFocusIn MtdFocusIn2 EvtFocusIn
BeginFunc MtdFocusIn2
call ObjLayout MtdChangeActiveTrain
EndFunc
最も単純化すると、前述の(1)〜(3)は上掲サンプルコードのように実装することが出来ます。
順序が前後しますが<選択可能な編成の編成スクリプト>から見ていきましょう。ここでは、FocusInイベント(この編成がアクティブになった時点で発生するイベント)でメソッドMtdFocusIn1を実行しています。このメソッドはget命令が1つあるのみで、レイアウトオブジェクトに所属するTrain変数ObjActiveTrainに自分自身(this)を代入しています。
これは、もし、レイアウト上のすべての編成にこのスクリプトが書かれていたとしたら、変数LAYOUT.ObjActiveTrainには常に現在選択されている=アクティブな編成の名前が入っていることを意味します。
表題に掲げた“編成を選べなくする”を実現するには、ユーザーに選択させたくない編成に<選択禁止の編成の編成スクリプト>を組み込みます。FocusInイベントでメソッドを実行するのは前述の<選択可能な編成の編成スクリプト>と同じですが、メソッドMtdFocusIn2は中身が異なります。先ほどあったget命令がなく(=つまり、この編成を選択してもObjActiveTrainの値は変わらない)その代わりにレイアウトオブジェクトのメソッドMtdChangeActiveTrainをcallしています。
最後に<レイアウトスクリプト>、特に<選択禁止の編成の編成スクリプト>からcallされるメソッドMtdChangeActiveTrainを見ていきましょう。
このメソッドが実行されるのは、ユーザーに選択させたくない編成がまさに選択された瞬間です。この編成を選択させたくないのですから、ここでは速やかに他の編成、具体的には変数ObjActiveTrainに記録されている編成=最後にアクティブだった選択可能な編成に、SetActiveTrain命令を使って切り換えれば良いことになります。
が、メソッドMtdChangeActiveTrainにはSetActiveTrain命令はなく、代わりにSetEventAfter命令があるのみです。そしてSetActiveTrain命令は、このSetEventAfter命令によって1ミリ秒後に実行されるメソッドMtdChangeActiveTrain2の中にあります。
今回試して初めて気づいたのですが、VRM4のFocusInイベントは、アクティブな編成の切り替えがおこなわれる直前に実行されるようです。もう少し詳しく言うと・・・
Step1. メニュー選択やF6キー押下、あるいは他のSetActiveTrain命令の実行をVRM4システムが気づく。
Step2. もし、FocusInイベントが設定されていれば、ここで実行される。
Step3. アクティブな編成の切り替えが処理される。
という順序を辿るようです。
イベントドリブン式のプログラミング経験がある方は、あぁ、そういうこともあるよな、とご理解いただけることでしょう。したがって、メソッドMtdChangeActiveTrainの中でSetActiveTrain命令を使っても、Step3.の処理で効果が上書きされるか、あるいはそもそも発動しないみたいです。とりあえず、見かけ上はそのように動いているように思われます。
この制約を回避するには、Step3.以降にSetActiveTrain命令を実行すればよいワケで、それをSetEventAfter命令による1ミリ秒後イベントで実現しています。これは、一般化して言えば「SetEventAfterによって、先行イベントからの連鎖を維持しつつ、callによる単純ネスト構造から離脱する」という手法で、他でもいろいろ応用が利くのですが、まぁ、それについてはまた追々。
最後の方の理屈がわからなくても、とりあえず上掲サンプルコードを然るべくコピー&ペーストすれば表題の動作をするはずですので、レイアウトコンテスト作品等に応用してみてください。なお、FocusInイベントの発動タイミングは製品付属のマニュアルに明記されていないので、今後突然変更されちゃったりするのかも知れません、あしからず。