2006/7/19

編成を解結する話・追補  VRMスクリプト禅問答
結論から言うと、SetEventAfter命令(おそらくは、オブジェクト指定可能なSetEvent系命令すべて)の仕様について、誤解していたらしいことに気づいたので報告しておきます。

×誤解

・SetEvent〜命令によって設定されたイベント(ある条件が整うと指定したメソッドを実行する仕掛け)は、SetEvent〜命令が記述されたオブジェクトに所属する。
・レイアウトオブジェクト(レイアウターメニューの[スクリプト編集])に記述されたSetEvent〜命令によって設定されたイベントは、レイアウトオブジェクトに所属するのであって、そこから実行されるメソッドがどのオブジェクトに存在しているかは関係がない。

△新たな知見(暫定)

・SetEvent〜命令の中でもオブジェクト参照が可能な命令(パラメーターを4つ要するもの/SetEventAfter, SetEventTime, SetEventTimer, SetEventKey)が設定するイベントは、命令が記述されたオブジェクトではなく、イベントによって実行されるメソッドが含まれるオブジェクトに所属する。
・レイアウトオブジェクトに記述されたSetEvent〜命令によって設定された編成オブジェクトのメソッドを実行するイベントは、レイアウトオブジェクトではなく、その編成オブジェクトに所属する(ように見える)。

既にこの時点で、ボクが何の話をしているのかわからない人の方が多そうな気もしたりしますが、とりあえず続けます。


まず、前回、そして45-50s氏の追試によって明らかになった疑問をまとめてみます。

・「連結後に再び解放する」というアクションを実現するには、SetEventCouple命令で連結イベントを拾い、そこからSetEventAfter命令で指定時間後にUncouple命令を含むメソッドを実行すれば良いだろう。
・SetEventCouple命令は自分自身(編成オブジェクト)に所属するメソッドしか実行できない。(1)Uncouple命令による編成分割に際しては「イベント、変数も複製されます(製品スクリプトマニュアル)」とあるので、編成オブジェクト内でSetEventAfterするのは避けたい。
・そこで、SetEventCoupleから実行されたメソッドから、レイアウトオブジェクトに所属するメソッドを実行し、そこでSetEventAfter命令を実行した。(2)このイベントはレイアウトオブジェクトに所属するから、編成を分割しても「複製」されないはずである。
・ところが、実際にはUncouple命令が実行される都度、SetEventAfter命令に指定されたメソッドが再実行されてしまう。
・SetEventKeyでイベントを設定しても同じことが起こる。

つまり、下線部(1)編成分割によってイベントが複製される、という仕様を回避すべく、下線部(2)SetEvent〜によるイベントはその命令が記述されたオブジェクトに所属するはず、という仮説に沿ってスクリプトを書いてみたら、その通りに動かなかった、という話です。

であれば、間違っているのは仮説の方です。そこで、前回のサンプルコードに手を加えてみました。ここまでの経緯から、自明と思い込んでいた下線部(2)が怪しいのは確実です。ですから、SetEvent〜命令から編成オブジェクトのメソッドを直接実行するのを止めて、一旦レイアウトオブジェクトのメソッドを経由させることにします。こんな具合に。
[[編成B]スクリプト]

前回と同じ)

[レイアウトスクリプト]
VarTrain NewTrain

//3秒後にレイアウトの「分割経由」メソッドを実行する
BeginFunc 分割準備
Var TimerID
SetEventAfter this 分割経由 TimerID 3000
EndFunc

//[編成B]の「分割」メソッドを実行する
BeginFunc 分割経由
call [編成B] 分割
EndFunc
案の定、[編成B]の「分割」メソッドが連続して実行されているように見える現象は収まりました。

ここから導き出される新たな知見が、冒頭に書いた「SetEvent〜命令の中でもオブジェクト参照が可能な命令が設定するイベントは、命令が記述されたオブジェクトではなく、イベントによって実行されるメソッドが含まれるオブジェクトに所属する」になります。つまり、「メソッドが連続して実行されているように見える現象」は、SetEventAfter命令によって「編成オブジェクトに対して」設定されたイベントが、Uncouple命令による分割によって「複製」されていたからである、と言うことになりましょう。

この新たな仮説を検証すべく、以下のような実験をおこなってみました。
[レイアウトスクリプト]
Var TimerID
SetEventTimer 編成 パンタ上下 TimerID 1000

[編成スクリプト]
Var KeyID
SerEventKey this 分割 KeyID z

//編成を分割する
BeginFunc 分割
VarTrain TmpTrain
KillEvent KeyID //イベントの無効化
Uncouple 1 TmpTrain
EndFucn

//パンタグラフを上げ下げする車両スクリプトを実行する
BeginFunc パンタ上下
CallCar パンタ昇降
EndFunc

[車両スクリプト]
BeginFunc パンタ昇降
Var Tmp
GetPantograph 0 Tmp
xor Tmp 1 // パンタグラフの状態を反転
SetPantograph 0 Tmp
EndFunc
上の例では、レイアウトスクリプトからSetEventTimer命令を使って、1秒毎に「編成」の「パンタグラフを上下させるメソッド」を実行するイベントを仕掛けています。
加えて、zキーを押すと編成が分割されるようになっています。パンタグラフ装備車を2両以上連ね、すべての車両に上掲の車両スクリプトを仕込んでビュワーを起動すると、1秒間隔でパンタグラフが上がったり下がったりを繰り返します。

ここでzキーを押すと、「編成」は「編成」と「編成_1」に分割されます。もし下線部(2)が正しければ、「編成」側のみでパンタグラフが上下し、「編成_1」側ではこのアクションが停止するはずですが、実際には両方の編成でパンタグラフの昇降が続きます。イベントが「複製」された証拠です。

一方、レイアウトスクリプトを・・・
Var TimerID
SetEventTimer this 経由メソッド TimerID 1000

//編成のパンタ上下メソッドを実行するメソッド
BeginFunc 経由メソッド
call 編成 パンタ上下
EndFunc
と書き換えると、zキーを押して編成を分割した際、「編成」側ではパンタの上下が続きますが、「編成_1」側ではパンタの上下が起こらなくなります。これは、レイアウトスクリプトから自分自身に所属するメソッドに対して設定されたイベントは、編成の分割時に複製されない=編成オブジェクトには所属していない、からであると解釈することが出来ます。

どう贔屓目に考えても、一般的なVRMユーザーにこの話が理解できるとは思えない・・・

それともボクのまとめ方が悪いのか?
長くなったのでこのへんで。
0

コメントを書く

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




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