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

今回からはシステムボタンを管理するプラグインの話ね。
SystemButtonPlugin クラスだよね。
そう。前回もちょっと言ったけど、SystemButtonPlugin クラスを一部書き換えないといけないから、 まずは SystemButtonPlugin クラスがどうやってシステムボタンを管理してるのかを見ていくね。
SystemButtonPlugin クラスって結構複雑だったような気がするんだけど…?
ん〜、確かにちょっと複雑かもね。
でも、やってることはそんなに複雑じゃないから、ちゃんと理解できると思うよ?
そうなの?
うん、多分ね。
じゃ、最初は SystemButtonPlugin クラスのメンバ変数から見ていくね。

SystemButtonPlugin クラスのメンバ変数>

var x = 510; // 初期 x 位置
var y = 300; // 初期 y 位置

var foreSeen = false// 表画面のボタンが可視か
var backSeen = false// 裏画面のボタンが可視か

var foreButtons = []; // 表画面のボタンの配列
var backButtons = []; // 裏画面のボタンの配列

まず、xy は最初のボタンを表示する位置。
最初のボタン?
このプラグインは複数のボタンを管理できるようになってるから、ボタンに順番があるんだ。
で、最初のボタンが (x, y) の位置に表示されて、 2番目のボタンは最初のボタンの右隣に表示されるように、自動的に位置が決められるの。
えっと、じゃあ、例えばボタンの幅が 100 ピクセルだったら、 最初のボタンが (510, 300) の位置に表示されて、2番目のボタンは (610, 300) に表示されるってこと?
そうそう、そういうこと。
次の foreSeenbackSeen は、 それぞれ表画面と裏画面のボタンが表示されてるかどうかを表すメンバ変数。
それって前の章で作った DatePlugin クラスの foreVisiblebackVisible みたいなもの?
※詳しくは §4.10 参照。
うん。foreSeenbackSeen の使い方も基本的に同じだよ。
じゃあシステムボタンもメッセージレイヤを非表示にすると非表示になるの?
うん、なるよ。
ていうか DatePlugin クラスは SystemButtonPlugin クラスを意識して作ったからね。
あ、そうだったんだ。
で、最後の foreButtonsbackButtons は、 それぞれ表画面に表示するボタンオブジェクトと裏画面に表示するボタンオブジェクトの配列。
表画面に表示する最初のボタンが foreButtons[0] ってこと?
ん、そうだよ。
ちなみに foreButtons = []; っていうのは foreButtons = new Array(); と同じ意味ね。
あ、そういえば…
ん?
メンバ変数が全部ここで初期化されてるけど、メンバ変数ってコンストラクタで初期化するんじゃないの?
あー、実はメンバ変数はこうやって普通の変数と同じように初期化できるんだ。
そうなの?
あ、じゃあコンストラクタで初期化するのとここで初期化するのは何か違いがあるとか?
ううん、別にどっちで初期化しても同じだよ。
えっ? じゃあ、メンバ変数ってどっちで初期化してもいいってこと?
うん。基本的にはどっちで初期化しても OK。
メンバ変数を宣言してるところで初期化する場合は、 メンバ変数が初期化されてからコンストラクタが呼び出されるようになってるんだ。
へぇ、そうなんだ。
じゃ、メンバ変数についてはこんなところだから、次はコンストラクタね。

<コンストラクタ>

function SystemButtonPlugin()
{
    // SystemButtonPlugin コンストラクタ

    createButtons(kag.fore.base, foreButtons); // 表画面のボタンを作成
    createButtons(kag.back.base, backButtons);

    realign(); // 再配置

    setObjProp(foreButtons, 'visible', foreSeen = false);
    setObjProp(backButtons, 'visible', backSeen = false);
        // 非表示に
}

う〜ん、コメントが書いてあるから何となく何してるかは解るけど、 全部メソッドを呼び出してるから、メソッドの中身を見てみないとよくわからないね…
だね。じゃあ createButtons メソッドから見ていこっか。
うん。

createButtons メソッド>

function createButtons(parent, array)
{
    // parent を親レイヤとしてボタンを作成し、array に登録する。
    // ボタンは表画面と裏画面の両方に対して作成されるので注意。

    // ボタンを追加するには、これを参考にして いろいろ追加してください。

    var obj;

    // ボタン 0 (セーブ)
    array.add(obj = new SystemButtonLayer(kag, parent, onSaveButtonClick));
    obj.loadImages('YesButton'); // save ボタン用画像を読み込む

    // ボタン 1 (ロード)
    array.add(obj = new SystemButtonLayer(kag, parent, onLoadButtonClick));
    obj.loadImages('NoButton'); // load ボタン用画像を読み込む

    // ここでは 'YesButton' とか 'NoButton' とかを読み込んでいますが
    // ちゃんとした画像を作成してちゃんとしたファイル名を指定すると
    // よいでしょう。ボタン用画像の作り方は button タグ用画像の作り方
    // と同じです。
}

createButtons メソッドは名前の通りボタンを作るメソッド。
このメソッドでセーブ用とロード用2つのボタンを作ってるんだ。
どっちも SystemButtonLayer クラスのオブジェクトになってるね。
コンストラクタの引数がどうなってるかわかる?
えっと、第1引数がこのボタンが所属するウィンドウで kag オブジェクトになってて、 第2引数はこのボタンの親レイヤで、createButtons メソッドの第1引数の parent になってるね。
parent は表画面用のボタンを作る時は kag.fore.base になって、 裏画面用のボタンを作る時は kag.back.base になるんだ。
それってボタンの親レイヤは背景レイヤになるってことだよね?
ん、そういうこと。
あと、第3引数はボタンが押された時に呼び出されるメソッド、だったよね?
そ。だから、セーブ用のボタンは押された時に onSaveButtonClick メソッドが呼び出されるわけね。
ちなみに onSaveButtonClick メソッドはこうなってるよ。

onSaveButtonClick メソッド>

function onSaveButtonClick()
{
    // セーブ ボタンが押された
    kag.saveBookMarkToFileWithAsk();
}

saveBookMarkToFileWithAsk って見たことないメソッドだね。
kag.saveBookMarkToFileWithAsk(); ってことは、このメソッドって kag オブジェクトのメソッド?
そだよ。
saveBookMarkToFileWithAsk メソッドは、フリーセーブモードの時に、 こういうファイル選択ダイアログボックスを表示して、栞を保存するメソッド。

「栞をはさむ」ダイアログボックス

あ〜、これってフリーセーブモードの時に、メニューの「栞をはさむ」を選択すると表示されるダイアログボックスだよね。
そうそう。
メニューの「栞をはさむ」を選択すると saveBookMarkToFileWithAsk メソッドが呼び出されるようになってるんだ。
あ、そうなんだ。
で、ロード用のボタンが押された時に呼び出される onLoadButtonClick メソッドも同じような感じになってるんだ。

onLoadButtonClick メソッド>

function onLoadButtonClick()
{
    // ロード ボタンが押された
    kag.loadBookMarkFromFileWithAsk();
}

今度は loadBookMarkFromFileWithAsk っていうメソッドが呼び出されてるけど、 これってやっぱりフリーセーブモードの時に、メニューの「栞をたどる」を選択するのと同じになるの?
うん。
loadBookMarkFromFileWithAsk メソッドはフリーセーブモードの時に、 こういうファイル選択ダイアログボックスを表示して、栞を読み込むメソッドだからね。

「栞をたどる」ダイアログボックス

ってワケで、結局セーブ用のボタンを押すのは、メニューの「栞をはさむ」を選択するのと同じで…
ロード用のボタンを押すのは、メニューの「栞をたどる」を選択するのと同じってことだね。
で、ボタンを作ったら createButtons メソッド の第2引数で指定されてる配列 array に追加してるわけね。
この array ってどんな配列なの?
array は表画面用のボタンを作る時には foreButtons で、 裏画面用のボタンを作る時には backButtons になるんだ。
あー、それってメンバ変数のボタンオブジェクトの配列だね。
そ。で後は loadImages メソッドでボタン用画像を読み込んで、ボタン完成。
セーブボタン用には "YesButton" っていう画像を読み込んで、 ロードボタン用には "NoButton" っていう画像を読み込んでるんだよね。
うん。まぁ、ボタン用画像のファイル名は適当に決めていいと思うけどね。
じゃ、次は realign メソッド。

realign メソッド(※コメントを追加してあります)>

function realign()
{
    // ボタンの再配置
    // このメソッドは、ボタンを x y 位置から横一列に配置する
    var fore, back, count, btn_x;

    count = foreButtons.count; // count にボタンの個数を代入
    btn_x = 0; // ボタンの表示オフセット
    for(var i = 0; i < count; i++)
    {
        var xpos = btn_x + x; // i 番目のボタンの x 方向の表示位置は、x にボタンの表示オフセットを足した位置になります
        var obj;

        obj = backButtons[i]; // obj に裏画面の i 番目のボタンオブジェクトへの参照を代入します
        obj.setPos(xpos, y);  // 裏画面の i 番目のボタンを (xpos, y) の位置に表示します
        obj.absolute = 2000000-3; // 裏画面の i 番目のボタンの重ね合わせ順序を 1999997(メッセージ履歴よりも奥)に設定します

        obj = foreButtons[i]; // obj に表画面の i 番目のボタンオブジェクトへの参照を代入します
        obj.setPos(xpos, y);  // 表画面の i 番目のボタンを (xpos, y) の位置に表示します
        obj.absolute = 2000000-3; // 表画面の i 番目のボタンの重ね合わせ順序を 1999997(メッセージ履歴よりも奥)に設定します

        btn_x += obj.width; // ボタンの表示オフセットに i 番目のボタンの幅を加算します
    }
}

これって何するメソッドなの?
realign メソッドはボタンを横に並べて表示するメソッドだよ。
さっき最初のボタンをメンバ変数の xy の位置に表示して、 2番目以降のボタンは自動的に表示する位置が決められるって言ったでしょ。
うん。
realign メソッドでボタンの表示位置を決めて、それぞれのボタンを決められた位置に表示するの。
あ、そうなんだ。
セーブ用のボタンとロード用のボタンを表示するときは count はいくつになると思う?
え〜っと、countforeButtonscount プロパティになってるから、ボタンの数ってことだよね…
ってことは… 2
そ。つまり for ブロックの中身は i=0i=1 で2回実行されるわけね。
i=0 の時にセーブ用ボタンの表示位置を決めて、 i=1 の時にロード用ボタンの表示位置を決めてるってことだよね?
ん、そうだよ。
じゃあ、i=0 の時に xpos はいくつになる?
ん〜… xposbtn_x + x で、 btn_x は最初 0 になってるよね…
あと、x510 に初期化されてるから、 0 + 510510 かな。
ん、そうだね。
じゃあ、セーブ用ボタンはどの位置に表示される?
セーブ用のボタンって、setPos メソッドで位置を設定してるんだよね?
そだよ。あと、表画面用のボタンも裏画面用のボタンも setPos メソッドの引数は同じだから、 同じ位置に配置されるってことだね。
じゃあ… y300 に初期化されてるわけだから、 setPos(xpos, y)setPos(510, 300) ってことになるから、 セーブ用のボタンは (510, 300) の位置に表示されるのかな?
うん、正解。
それじゃセーブ用のボタンの幅、つまり obj.width50 ピクセルだったとしたら、ロード用のボタンはどこに表示されると思う?
ん〜っと…ロード用ボタンの xpos を計算する前に、 btn_x += obj.width; が実行されるよね。
obj.width50 ってことは、 btn_x50 になるよね。
だね。
じゃあ今度は xpos50 + 510560 になって、 y はそのままだから、ロード用のボタンは (560, 300) に表示されるんじゃないかな?
そ。つまり、ロード用のボタンはこんなふうにセーブ用のボタンの右隣に表示されるってワケ。

<セーブ・ロードボタンの表示位置>

ボタンの表示位置

で、ロードボタンの位置を設定した後は btn_x にロード用ボタンの幅が加算されるから、 もしボタンが3つあったとしたら、3番目のボタンはロード用ボタンの右隣に表示されるわけね。
ってことは、ボタンはずっと横につながって一列に表示されるってことだよね?
ん、そういうこと。
なるほどね〜。
あ、一つ質問!
ん?
absolute っていうプロパティ?…も設定してるみたいだけど、 この absolute って何なの?
absolute プロパティはレイヤの重ね合わせの順序を表すプロパティだよ。
じゃあそれって image タグの index 属性みたいなもの?
みたいなもの、っていうか absolute プロパティと index 属性は全く同じものだよ。
え、そうなの?
うん。KAG の属性と TJS のプロパティで違う名前になってるだけ。
へぇ、そうなんだ。
メッセージ履歴を表示するレイヤの absolute プロパティの値が 2000000 だから、 ボタンの absolute プロパティを 2000000-3 に設定することで、 ボタンをメッセージ履歴より奥に表示するようにしてるわけだね。
3 引くのって何か意味があるの?
うーん、どうだろ。別に 3 じゃないといけないってコトはないんじゃないかな。
じゃ別の値でもいいってこと?
うん、別の値の方がいいって思うんだったら変えても OK。
まぁでも特に問題がなければこの値にしといていいんじゃない?
そっか、わかった。
それじゃこれで realign メソッドは一通りチェックできたから、 次は setObjProp メソッドを見ていこっか。

setObjProp メソッド>

function setObjProp(array, member, value)
{
    // array の各メンバのプロパティの設定
    for(var i = array.count - 1; i >= 0; i--) array[i][member] = value;
}

シンプルなメソッドだね。
見た目はね〜。
えっ? それってどういうこと?
実はこのメソッド、単純そうに見えてちょっとややこしいんだ。
え、そうなの?
コンストラクタでこんなふうに setObjProp メソッドが呼び出されてるでしょ。

setObjProp(foreButtons, 'visible', foreSeen = false);

このときに setObjProp メソッドのスクリプトがどんなふうに実行されるかわかる?
引数の arraymembervalue がそれぞれ foreButtons'visible' と…え〜っと、これって確か false になるんだよね?
そ。foreSeen = false を第3引数に指定すると、 setObjProp メソッドが呼び出される前にまず foreSeenfalse が代入されて、 その結果この式全体の値が false になるから、 setObjProp メソッドが呼び出される時には第3引数は false になって、 valuefalse になるよ。
な、なんかややこしいね…
まぁ先に foreSeen = false; を実行してから setObjProp(foreButtons, 'visible', foreSeen); を実行するって考えればいいと思うよ。
なるほどね。
えっと、arraycount プロパティは 2 だから、 for ブロックの中身は2回実行されるよね。
i の初期値が array.count-1 になってて、 i0 以上の間 i をデクリメントするから、 今回は i=1, i=0 の順になって、ロードボタンが先で、セーブボタンが後に設定されるね。
で、i=1 の時は…
引数の値を当てはめると、foreButtons[1]['visible'] = false; になるよね…?
ん、そうだね。
なんか見たことない書き方なんだけど…?
foreButtons[1] が何を指してるかは解るよね?
ロード用ボタンオブジェクトだよね?
そうそう。じゃあ、さっきの realign メソッド みたいに、 obj っていう変数を作って、obj = foreButtons[1]; って感じで obj にボタンオブジェクトへの参照を代入したとするね。
うん。
そしたら、foreButtons[1]['visible']obj['visible'] って表せるでしょ。
そだね。
この書き方って前に見たことない?
え? う〜ん……
あ、そういえば辞書配列って確かこんな書き方できなかったっけ?
ん、それそれ。
辞書配列だと『辞書配列名['要素名']』って書くのと 『辞書配列名.要素名』って書くのは同じだったよね。
うん、そうだったよね。
実は普通のクラスのオブジェクトもこういう書き方ができるんだ。
え、そうなの?
うん。つまり、obj['visible'] って書くのと、 obj.visible って書くのは同じ意味になるんだ。
あ、じゃあ obj['visible'] でオブジェクトの visible プロパティを設定できるんだ?
ん、そのとーり。
だから foreButtons[1]['visible'] = false; で表画面のロード用ボタンの visible プロパティを false に設定してたってワケ。
なるほどね〜。
つまり、setObjProp メソッド は、 array っていう配列に含まれてるオブジェクトの member っていうプロパティの値を value に設定する、っていうメソッドなの。
じゃあ、コンストラクタsetObjProp メソッド を呼び出してるのって、 表画面と裏画面の全部のボタンの visiblefalse にしてる、 ってコトなんだよね?
そういうこと。
これでコンストラクタでやってることは解ったよね。
最初に createButtons メソッド を呼び出してボタンを作って、 それから realign メソッド を呼び出してボタンを横に並べて、 最後に setObjProp メソッド を呼び出してボタンの visiblefalse にしてるんだよね。
ん、OK。
んじゃ今回はここまでにして、残りのメソッドは次回見ていくことにするね。
は〜い!
それじゃ、また次回ね!


前へ | TOP | 次へ