Section 5.9 システムボタンプラグイン(その2)

今回は SystemButtonPlugin クラスの残りのメソッドを見ていくね。
残ってるのってイベントハンドラだよね?
ん、大体はね。
でもその前にまず setOptions メソッドから。

setOptions メソッド>

function setOptions(elm)
{
    // オプションを設定
    setObjProp(foreButtons, 'visible', foreSeen = +elm.forevisible) if elm.forevisible !== void;
    setObjProp(backButtons, 'visible', backSeen = +elm.backvisible) if elm.backvisible !== void;
    var poschanged = false;
    (x = +elm.left, poschanged = trueif elm.left !== void;
    (y = +elm.top, poschanged = trueif elm.top !== void;
    if(poschanged) realign(); //  表示位置の変更
}

setOptions メソッドって、4章で作った DatePlugin クラスにもあったよね?
ってことは、これってマクロから呼び出すメソッドなの?
§4.9参照。
うん。DatePlugin クラスのその辺の仕様は SystemButtonPlugin クラスに合わせて作ったからね。
だからマクロ定義もほぼ同じ。

sysbtopt マクロ>

@macro name="sysbtopt"
@eval exp="systembutton_object.setOptions(mp)"
; mp がマクロに渡された属性を示す辞書配列オブジェクト
@endmacro
※変数 systembutton_object は systembutton.ks 内で宣言されている SystemButtonPlugin クラスのオブジェクトです。

setOptions メソッドの引数 elm に指定できる要素、 つまり sysbtopt マクロに指定できる属性はこれね。

setOptions メソッドの引数 elm に指定できる要素(sysbtopt マクロに指定できる属性)>
要素(属性)名設定項目
forevisible表画面のボタンの表示・非表示true:表示、false:非表示
backvisible裏画面のボタンの表示・非表示true:表示、false:非表示
left1番目のボタン(セーブ用ボタン)の左端位置数値
top1番目のボタン(セーブ用ボタン)の上端位置数値

確かに dateopt マクロ に似てるね。
まぁ sysbtopt マクロの方がオリジナルなんだけどね。
じゃ setOptions メソッド を見ていくね。
はーい。
まず最初に setObjProp メソッド を使って表画面と裏画面のボタンの表示状態を設定してるよね。
ねぇ、なんか if がヘンな位置にあるんだけど…?
あぁ、これは if 演算子だよ。
if 演算子?
それって普通の if と何か違うの?
今まで使ってた if は演算子じゃなくて制御構文を作るためのキーワードだから、 厳密に言うと違うものなんだけど…まぁ直感的には今まで使ってきた ifif 演算子もほぼ同じって考えてもいいかな。
えっと…結局どういうことなの?
if 演算子の後ろに条件が書いてあるでしょ?
うん。
elm.forevisible !== void って書いてあるよね。
この条件が成立してれば、setObjProp メソッドが呼び出されるの。
それってつまり…

if(elm.forevisible !== void)
    setObjProp(foreButtons, 'visible', foreSeen = +elm.forevisible);

…ってこと?
そ。こう書いても同じ意味になるよ。
じゃあ if 演算子って普通の if と同じ使い方ができるってこと?
ううん、そうじゃないよ。
まず if 演算子はブロックが作れないんだ。 だから条件が成立した時に実行されるのは条件の左側に書いてある1文だけ。
つまりこんな使い方はできないってこと。

{
    a += 10;
    b += a;
if a < 100;  // if 演算子はブロックが作れないのでエラー!

あ、そうなんだ。
あと if 演算子はあっても else 演算子とかはないから、 条件が成立しなかった時に実行したいスクリプトがある場合にも使えないよ。
まぁ else が要らない単純な条件分岐の時には if 演算子を使うとちょっと簡単に書けるよね。
ふぅん…なるほどねぇ。
じゃ setOptions メソッド に戻るね。
setObjProp メソッドを呼び出してる部分はわかるよね?
えっと… elmforevisible 要素が指定されてたら、 表画面のボタンの visible プロパティを forevisible 要素に指定されてる値にして、 backvisible 要素が指定されてたら、 裏画面のボタンの visible プロパティを backvisible 要素に指定されてる値にするんだよね?
ん、そう。
あと setObjProp メソッド を呼び出す時に foreSeenbackSeen メンバ変数の値も設定してるよね。
このメンバ変数って onMessageHiddenStateChanged メソッドで使うんだよね。
うん、§4.10 の時と同じ使い方だよ。
で、その後は位置の設定だね。
えっと、さっき if 演算子はブロックが使えないって言ってたよね?
うん、そうだけど?
elm.left !== void が条件になってる所って、if 演算子の左側で xposchanged を設定してるけど、 これってブロックになってるんじゃないの?
ううん、これはブロックじゃないよ。
えっ、そうなの?
ブロックは {} で囲まれた部分のこと。
if 演算子の左側は () で囲まれてるでしょ?
あ、ホントだ。
でも x = +elm.leftposchanged = true って別々の文でしょ?
普通はそうなんだけど、順次演算子が使われてるから、これは2つで1つの文とみなされるの。
順次演算子?
x = +elm.leftposchanged = true, (カンマ)でつないであるでしょ。
この , が順次演算子で、これで複数の文をつなげると、全体として1つの文とみなされるんだ。
ちなみに実行順序は左から右だから、x = +elm.left が実行された後に poschanged = true が実行されるよ。
う〜ん…なんかすごくややこしいね…
まぁ、こうすれば1行で条件文が書けるんだけど、もちろん普通の if を使って…

if(elm.left !== void)
{
    x = +elm.left;
    poschanged = true;
}

if(elm.top !== void)
{
    y = +elm.top;
    poschanged = true;
}

…って書いてもいいよ。
そうだね。こっちのが分かりやすいかも。
じゃ、このスクリプトが何してるか説明してみて。
えっとね… elmleft 要素が指定されてる時は xelm.left を数値にした値を代入して、 top 要素が指定されてる時は yelm.top を数値にした値を代入するんだよね。
あと、lefttop のどっちかが指定されてると、 poschangedtrue になるよね。
ん。じゃあ、poschanged って何のための変数かわかる?
これって lefttop が指定してある時だけ realign メソッドを呼び出してボタンの位置を設定するための変数だよね。
そ。§4.9 でも dateChanged っていう変数を使って同じようなことをやったよね。
うん。
んじゃこれで setOptions メソッド は OK だから、 次はイベントハンドラの方を見ていくね。
まずは onStore メソッドから。

onStore メソッド>

function onStore(f, elm)
{
    // 栞を保存するとき
    var dic = f.systemButtons = %[];
        // f.systemButtons に辞書配列を作成
    dic.foreVisible = foreSeen;
    dic.backVisible = backSeen;
    dic.left = x;
    dic.top = y;
        // 各情報を辞書配列に記録
}

これって fsystemButtons っていう要素に辞書配列を作って、 foreSeenbackSeenxy の値をそれぞれ foreVisiblebackVisiblelefttop っていう要素に書き込んでるんだよね。
そうそう。だからボタンの表示状態と表示位置が記録されてる systemButtons の中身がセーブデータに保存されるってわけだね。
これも4章でやったのと似てるね。
§4.11参照。
うん。DatePlugin クラスのイベントハンドラも大体 SystemButtonPlugin クラスのに似せて作ったからね。
じゃ次は onRestore メソッド。

onRestore メソッド>

function onRestore(f, clear, elm)
{
    // 栞を読み出すとき
    var dic = f.systemButtons;
    if(dic === void)
    {
        // systemButtons の情報が栞に保存されていない
        setObjProp(foreButtons, 'visible', foreSeen = false);
        setObjProp(backButtons, 'visible', backSeen = false);
    }
    else
    {
        // systemButtons の情報が栞に保存されている
        setOptions(%[ forevisible : dic.foreVisible, backvisible : dic.backVisible,
            left : dic.left, top : dic.top]);
            // オプションを設定
    }
}

これも4章で作ったのと似てると思うから、どうなってるか説明してみて。
§4.12参照。
うん、おっけー。
…あっ、その前にちょっと聞きたいんだけど。
ん、なに?
setOptions メソッドの引数の辞書配列の初期化の仕方がいつもと違うんだけど、 これって何か特別な初期化とかしてるの?
ううん、別に特別な初期化とかじゃないよ。
辞書配列の初期化のやり方には今まで使ってた %["要素名" => 値] っていう形式の他に、 ここで使われてる %[要素名 : 値] っていう形式があるんだ。
えっと、それってつまり、

setOptions(%[ forevisible : dic.foreVisible, backvisible : dic.backVisible, left : dic.left, top : dic.top]);

って書くのと…

setOptions(%[ "forevisible" => dic.foreVisible, "backvisible" => dic.backVisible, "left" => dic.left, "top" => dic.top]);

って書くのは同じってこと?
そ。そういうこと。
あとはわかる?
最初に条件分岐のところでセーブデータがあるかどうかチェックして、 セーブデータがなかったら dicvoid になるから if の方のブロックの中身が実行されて、 セーブデータがあったら else の方のブロックの中身が実行されるんだよね。
うんうん。
それで、セーブデータがなかったら setObjProp メソッド を呼び出して、表画面と裏画面のボタンの visiblefalse にして、 セーブデータがあったら setOptions メソッド を呼び出して、セーブデータに保存されてる値を使ってボタンの表示状態と表示位置の設定をしてるんだよね。
うん、バッチリ。
でしょ〜。
それじゃ次は onStableStateChanged メソッドね。

onStableStateChanged メソッド>

function onStableStateChanged(stable)
{
    // 「安定」( s l p の各タグで停止中 ) か、
    // 「走行中」 ( それ以外 ) かの状態が変わったときに呼ばれる

    // 走行中は各ボタンを無効にする
    setObjProp(foreButtons, 'enabled', stable);
    setObjProp(backButtons, 'enabled', stable);
}

このメソッドって4章で出てこなかったよね?
うん、DatePlugin クラスには必要なかったからね。
onStableStateChanged メソッド は、 安定状態から走行状態に切り替わった時と、走行状態から安定状態に切り替わった時に呼び出されるメソッドなんだ。
安定状態とか走行状態って?
安定状態っていうのは s タグ、l タグ、 p タグでシナリオの進行が停止してる状態のこと。
走行状態っていうのはそれ以外の状態のことね。
へぇ、そういうのを安定状態とか走行状態って呼ぶんだ。
えっと、このメソッドって何のために使ってるの?
安定状態の時だけボタンが使えるようにするために使ってるの。
setObjProp メソッド でボタンの enabled を設定してるでしょ。
ほんとだ。
あ、第3引数の stable っていうのは?
stableonStableStateChanged メソッドの引数で、 安定状態になった時には true になってて、走行状態になった時には false になってるんだ。
そっか。だから安定状態の時だけ enabledtrue に設定されてボタンが使えるようになるんだね。
そういうこと。
じゃ次は onMessageHiddenStateChanged メソッド。

onMessageHiddenStateChanged メソッド>

function onMessageHiddenStateChanged(hidden)
{
    // メッセージレイヤがユーザの操作によって隠されるとき、現れるときに
    // 呼ばれる。メッセージレイヤとともに表示/非表示を切り替えたいときは
    // ここで設定する。
    if(hidden)
    {
        setObjProp(foreButtons, 'visible'false);
        setObjProp(backButtons, 'visible'false);
    }
    else
    {
        // foreSeen, backSeen は、ボタンが本来表示中であったかどうかを記録している
        setObjProp(foreButtons, 'visible', foreSeen);
        setObjProp(backButtons, 'visible', backSeen);
    }
}

これって DatePlugin クラスの onMessageHiddenStateChanged メソッド と同じようにボタンの visible を設定してるよね?
うん。メッセージレイヤが非表示になった時はボタンの visiblefalse にして、 メッセージレイヤが表示された時は、表画面のボタンと裏画面のボタンの visible をそれぞれ foreSeenbackSeen に設定することで、 非表示になる前の状態に戻してるわけだね。
じゃあ次は onCopyLayer メソッド。

onCopyLayer メソッド>

function onCopyLayer(toback)
{
    // レイヤの表←→裏の情報のコピー

    // backlay タグやトランジションの終了時に呼ばれる

    // ここでレイヤに関してコピーすべきなのは
    // 表示/非表示の情報だけ

    if(toback)
    {
        // 表→裏
        setObjProp(backButtons, 'visible', foreButtons[0].visible);
        backSeen = foreSeen;
    }
    else
    {
        // 裏→表
        setObjProp(foreButtons, 'visible', backButtons[0].visible);
        foreSeen = backSeen;
    }
}

コピーしてるのって visible 関係だけだね。
DatePlugin クラスの onCopyLayer メソッド だと画像と位置とかもコピーしてたけど、システムボタンの場合は表画面と裏画面の画像と位置はいつも同じだから、表示状態をコピーするだけで OK なんだ。
そっか、なるほどね。
じゃ、最後は onExchangeForeBack メソッド。

onExchangeForeBack メソッド>

function onExchangeForeBack()
{
    // 裏と表の管理情報を交換

    // children = true のトランジションでは、トランジション終了時に
    // 表画面と裏画面のレイヤ構造がそっくり入れ替わるので、
    // それまで 表画面だと思っていたものが裏画面に、裏画面だと思って
    // いたものが表画面になってしまう。ここのタイミングでその情報を
    // 入れ替えれば、矛盾は生じないで済む。

    // ここで表画面、裏画面のレイヤに関して管理すべきなのは
    // foreButtons と backButton 、foreSeen と backSeen の変数だけ
    var tmp;

    tmp = backButtons;
    backButtons = foreButtons;
    foreButtons = tmp;

    tmp = backSeen;
    backSeen = foreSeen;
    foreSeen = tmp;
}

まぁこれも基本的に DatePlugin クラスの onExchangeForeBack メソッド とやってることは同じだね。
でもなんかこっちの方が複雑だよ?
こっちの onExchangeForeBack メソッドは入れ替えの演算子(<->)を使わずに、 tmp っていう変数を使って値を入れ替えてるだけで、やってることは同じだよ。
ちなみに、<-> 演算子を使うとこうなるよ。

<-> 演算子を使った onExchangeForeBack メソッド>

function onExchangeForeBack()
{
    foreButtons <-> backButtons;

    foreSeen <-> backSeen;
}

あ、ホントだ。
DatePlugin クラスの時みたいになってるね。
さてと、これで SystemButtonPlugin クラスを一通り見終わったから、 次回はこれをカスタマイズして、§5.1 のメッセージウィンドウを作っていくね。
セーブとロードの他にもメッセージスキップとかオートモードとかのボタンもつけるんだよね?
そ。あとボタンは §5.6§5.7 で作ったクラスを使って作るから。
うん、りょ〜かい!
それじゃ、また次回ね!


前へ | TOP | 次へ