7.4 選択肢用レイヤ(その3)

今回は ButtonLinkLayer クラスの残りのメソッドを見ていくね。
は〜い。
まずは drawState メソッドからね。
drawState メソッドって、 マウスカーソルが乗った時とかクリックされた時に画像を切り替える処理をやってるメソッドだったよね?
§5.4§5.6 参照。
そうそう。
あと、今回はテキストの色も変えなくちゃいけないから、ButtonLayer クラスの drawState メソッドをオーバーライドするの。
あ、それも drawState メソッドでやるんだ。
テキストもマウスカーソルが乗ったりクリックされたりする時に切り替えるからね。
で、これがスクリプト。

drawState メソッド>

// ButtonLayer クラスの drawState メソッドをオーバーライドします
function drawState(s)
{
    // 状態 s に対応する画像とテキストを描画
    // s :  0 : 普通の状態
    //      1 : ボタンが押された状態
    //      2 : ボタンの上にマウスカーソルがある状態

    // 画像の表示を更新します
    super.drawState(s);

    // テキストの表示を更新します
    with(textLayer)
    {
        // レイヤをクリアします
        .fillRect(0, 0, .width, .height, 0x00000000);
        // テキストをレイヤの中央に表示します
        var textWidth = .font.getTextWidth(text);
        var textHeight = .font.getTextHeight(text);
        .drawText((.width - textWidth) \ 2, (.height - textHeight) \ 2, text, textcolor[s]);
    }
}

drawState メソッドで画像とテキストを更新するわけだけど、 画像の更新はスーパークラスの drawState メソッドがやってくれるから、 super.drawState(s); で OK。
スーパークラスってことは ButtonLayer クラスの drawState メソッドを呼び出すってことだよね。
ButtonLayer クラスの drawState メソッドは前に見てるから、どんな処理をしてるかはわかるよね?
引数の s がボタンの状態を表してるから、 s の値に応じてレイヤの imageLeft の値を設定して画像を切り替えるんだよね。
そうそう。
じゃ次はテキストの表示を更新する処理を見てこうと思うんだけど、 with って覚えてる?
えっと、前に何回か使ったよね。
確か、with(textLayer) っていうブロックを作ると、 そのブロックの中で textLayer を省略できるんじゃなかったかな。
だから、.fillRecttextLayer.fillRect っていう意味になるんだよね?
ん、そう。§3.3 とかで使ったよね。
じゃ最初の fillRect メソッドで何やってるかはわかる?
0x00000000 は透明色だから、textLayer 全体を透明色で塗りつぶして、 元々書いてあったテキストを消してるんだよね。
§3.10 参照。
そうだね。
じゃその次のテキストを表示してる部分はわかる?
drawText メソッドでテキストをレイヤに書き込んでるのはわかるんだけど、 なんか色々ややこしい計算してるみたいだよね。
でもこれとおんなじ事は前にもやってるよ。
え、そうだっけ?
§3.5 で、文字をレイヤの真ん中に表示した時に同じようにしたでしょ。
あ〜、確かに drawText メソッドの第1引数と第2引数があの時とおんなじになってるね。
つまり、テキストはテキスト用レイヤ(textLayer)の真ん中に表示するし、 テキスト用レイヤは画像用レイヤと同じサイズだから、選択肢の画像の真ん中にテキストが表示されてるように見えるわけ。
なるほどねぇ。
えと、第3引数の text と第4引数の textcolor は確かメンバ変数だったよね?
そうだよ。text が表示するテキストで、textcolor が文字の色ね。
前回作った setOptions メソッドで設定できるようにしたよね。
あ、もしかして textcolor を配列にしてたのって、 s を添え字に使えるようにするためだったの?
うん。s は通常時・クリックしてる時・マウスカーソルが乗ってる時にそれぞれ 0, 1, 2 になるから、 textcolor[s] でそれぞれの状態の時のテキストの色が取得できるようにしたんだ。
そっか。それだとテキストの色を指定する時に条件分岐とかしなくていいもんね。
それじゃこれで drawState メソッドは OK かな?
うん。
じゃ次はイベントハンドラを見ていくね。
まずは onMouseUp メソッドから。

onMouseUp メソッド>

function onMouseUp(x, y, button, shift)
{
    if(enabled && button == mbLeft)
    {
        // clickse が指定されていれば効果音を再生します
        if(clickse !== void)
            kag.se[clicksebuf].play(%["storage" => clickse, "loop" => false]);

        // exp が指定されていれば TJS 式として実行します
        if(exp !== void)
            Scripts.eval(exp);

        // クリックされた時に実行するメソッドを this を引数にして呼び出します
        onClickFunction(this);
    }

    // スーパークラスの onMouseUp メソッドを呼び出します
    super.onMouseUp(...);
}

最初の if のとこで何をチェックしてるかわかる?
enabled が真で、さらに buttonmbLeft の時だから…
ボタンが押せる状態になってて、マウスの左ボタンがクリックされた時に if ブロックの中身が実行されるってことかな。
ん、そういうことだね。
じゃ if ブロックの中身の方はどうなってる?
えっと、最初に clicksevoid じゃなかったら、 clicksebuf 番の効果音バッファを使ってクリックされた時の効果音を再生してるね。
これは §5.7 でも同じことやったよね。
ちなみに clickseclicksebufsetOptions メソッドで設定できるよ。
だね。これはちゃんと覚えてるよ。
じゃあとは exp の処理だね。
expvoid じゃなかったら、 Scripts.eval っていうメソッドを呼び出してるみたいだけど、これって今までに見たことないメソッドだよね?
名前からどんなメソッドかなんとなくわからない?
eval っていう名前だから、eval タグと何か関係ありそうだけど…?
そ。Scripts クラスの eval メソッドは、 eval タグを実行すると呼び出されるメソッドだよ。
あ、そーなんだ。
それじゃあ引数の expexp 属性ってこと?
うん、そーいうこと。
つまり expvoid じゃなかったら、 eval タグを実行するみたいに exp に代入されてる文字列を TJS 式として実行するってこと。
なるほどね。
で、あとは SystemButtonLayer クラスでもやってたように、 クリックされた時に実行するメソッドを呼び出してるわけね。
§5.6 参照。
このオブジェクトへの参照を引数にして onClickFunction を呼び出すんだね。
そう。んじゃ次は onMouseEnter メソッドね。

onMouseEnter メソッド>

function onMouseEnter()
{
    // enterse が指定されていれば効果音を再生します
    if(enterse !== void)
        kag.se[entersebuf].play(%["storage" => enterse, "loop" => false]);

    // onenter が指定されていれば TJS 式として実行します
    if(onenter !== void)
        Scripts.eval(onenter);

    // スーパークラスの onMouseEnter メソッドを呼び出します
    super.onMouseEnter(...);
}

見ての通り、onMouseEnter メソッドでやってることは onMouseUp メソッドでやってることと大体同じだよ。
entersevoid じゃなかったら効果音を再生して、 あと onentervoid じゃなかったら eval メソッドで TJS 式として実行するんだね。
そ。このメソッドは特に問題ないよね?
うん。
じゃ次いくね。
次は onMouseLeave メソッドなんだけど、これもやってることはほとんど同じだよ。

onMouseLeave メソッド>

function onMouseLeave()
{
    // leavese が指定されていれば効果音を再生します
    if(leavese !== void)
        kag.se[leavesebuf].play(%["storage" => leavese, "loop" => false]);

    // onleave が指定されていれば TJS 式として実行します
    if(onleave !== void)
        Scripts.eval(onleave);

    // スーパークラスの onMouseLeave メソッドを呼び出します
    super.onMouseLeave(...);
}

onMouseEnter メソッドの enterseentersebufonenterleaveseleavesebufonleave に置き換わってるだけだね。
だからこのメソッドも特に説明は必要ないよね。
うん、だいじょぶだよ。
これで ButtonLinkLayer クラスは完成だよ。
あ、そーなんだ。
まだ選択肢のプラグインは出来てないけど、とりあえず ButtonLinkLayer クラスのオブジェクトを実際に作ってみよっか。
うん、そーだね。
選択肢がどんな感じになるのかちょっと見てみたいし。
じゃちょっと仮のマクロ作っとくね。
仮のマクロって?
選択肢の項目、つまり ButtonLinkLayer クラスのオブジェクトを作ったり、 setOptions メソッドを呼び出して設定したりするのは、最終的にはマクロを使ってやるつもりだから。
あ、そーなんだ。
選択肢の項目を作るマクロは、ホントは選択肢を管理するプラグインのメソッドを呼び出すようになってるんだけど、 今はまだそのプラグインができてないから、とりあえず仮のマクロで選択肢の項目を作ってみるってワケ。
なるほど、それで仮なんだね。
で、これが選択肢の項目を作る blink マクロ(仮)だよ。

<選択肢の項目を作成する blink マクロ(仮)>

[macro name=blink]
; ButtonLinkLayer クラスのオブジェクトを作成します
[eval exp="tf.buttonlink = new ButtonLinkLayer(kag, kag.fore.base, function(obj){kag.process(obj.storage, obj.target, obj.countpage);}, mp)"]
[endmacro]

最初の eval タグで ButtonLinkLayer クラスのオブジェクトを作ってるみたいだけど、 なんか引数がややこしいね。
確かに第3引数に式中関数を指定してるから長くなっちゃってるけどね。
基本的に §5.2SystemButtonLayer クラスのオブジェクトを作った時とおんなじやり方だよ。
※式中関数については §1.19 参照。
えーっと、第3引数の式中関数って…

SystemButtonLayer クラスのコンストラクタの第3引数に指定されている式中関数>

function(obj)
{
    kag.process(obj.storage, obj.target, obj.countpage);
}

こーなってるんだよね?
ん、ちなみにこの式中関数は SystemButtonLayer クラスの onMouseUp メソッドから this を引数にして呼び出されるから、 引数の objtf.buttonlink と同じって考えて OK だよ。
じゃあ obj.storageobj.targetobj.countpagesetOptions メソッドで設定した値になってるんだろーけど…
kag.process って初めて見るメソッドだよね?
process メソッドはスクリプトを指定した位置から実行するメソッドだよ。
第1引数と第2引数にはジャンプ先のファイル名とラベル名を指定して、 第3引数には指定されたラベルにジャンプする時に、ジャンプする前にいた場所のラベルを読んだとみなすかどうかを指定するの。
それって jump タグの storage 属性と target 属性と countpage 属性とおんなじってこと?
ん、まぁ process メソッドを呼び出すのは jump タグを実行するのと基本的にはおんなじだね。
そっか、わかった。
あとは第4引数に mp を指定してるから、コンストラクタの中で setOptions メソッドを呼び出す時に mp が引数になるんだと思うけど、 この mp って確かマクロに指定されてる属性値を調べるのに使うんだったよね?
うん、mp はマクロに指定されてる属性値が記録されてる辞書配列だよ。
だから、例えば mp.storagestorage 属性に指定されてる内容が取得できるわけね。
mp については §4.8 参照。
あ、そうそう。そんな感じだったよね。
あと storage 属性が指定されてない時は mp.storagevoid になってるんだったよね?
ん、そうだよ。
これで blink マクロで何やってるかは解った?
うん、おっけー。
じゃあと first.ks もちょっとチェックしとくね。

ButtonLinkLayer クラスのオブジェクトを作るための first.ks>

;ButtonLinkLayer クラスの定義を読み込みます
[call storage="ButtonLinkLayer.ks"]

;選択肢の項目を作成します
[blink graphic="buttonlink_bg" left=50 top=222 text="Click Me!" target=*clicked]
;クリックされるのを待ちます
[s]

;クリックされた時にジャンプしてくるラベル
*clicked
;選択肢の項目を非表示にします
[eval exp="tf.buttonlink.visible = false"]

クリックされました。
[s]

え〜っと、最初に call タグで ButtonLinkLayer.ks を読み込んでるね。
ButtonLinkLayer.ks に ButtonLinkLayer クラスの定義を書いてるから、これは必要だよね。
それから blink タグで選択肢の項目を作ってるんだね。
指定できる属性はいっぱいあったけど、ここで指定されてるのって graphiclefttoptexttarget だけだね。
もちろん他の属性を指定して実行してもいいんだけど、 まぁテストなんだし、これくらいでいいでしょ。
そーだね。
あ、あと graphic 属性に指定してる画像ってどんななの?
こんな感じだよ。

<選択肢の背景用画像(buttonlink_bg.png)>

※画像は縮小表示しています。

これって button タグ用の画像みたいに、 通常時・クリックされてる時・マウスカーソルが乗ってる時に表示する画像をつなげてるんだよね?
そうだよ。
ん〜、あとの属性は大体わかるから、次は s タグかな。
これって link タグで選択肢を表示する時みたいにしてるってことだよね?
ん、ここに s タグを置いとかないとシナリオが先に進んじゃって、 選択肢がクリックされる前に次のメッセージが表示されちゃったりするからね。
だね。
その後にある *clicked っていうラベルが、 選択肢がクリックされた時のジャンプ先なんだよね。
blink タグtarget 属性が *clicked になってるし、storage 属性は指定されてないもんね。
そうそう。
えっと、ラベルのすぐ後の eval タグで選択肢を非表示にしてるみたいだけど、 これっていちいちジャンプ先のラベルの後に書かなくちゃいけないの?
ラベルの後に選択肢を非表示にするスクリプトを書かなくちゃいけないのは今回だけだよ。
後で選択肢を管理するプラグインを作る時に、 プラグイン側でラベルにジャンプする前に自動的に選択肢を非表示にするように作るから、 最終的にはラベルの後に選択肢を非表示にするスクリプトを書く必要はなくなるよ。
あ、そーなんだ。
さすがに全部のジャンプ先ラベルに選択肢を非表示にするスクリプトを書くのは面倒だからね。
だよねぇ。
これで first.ks の内容は大体わかった?
うん。
それじゃ実行してみよっか。
必要なファイルはここに置いとくから。
はーい!

<実行結果>

表示されたウィンドウと選択肢

うん、ちゃんと選択肢の項目が表示されてるね。
…1個しかないからあんまり選択肢っぽくないけど。
んー、確かにね。まぁ今回はテストってことで。
じゃとりあえずクリックしてみて。
うん、わかった。

<マウスカーソルが選択肢の上に乗った時の表示>

あ、マウスカーソルが乗ったら画像と文字の色が変わったね。
ちゃんと設定通りになってるね。
それじゃ、クリック、と。

<選択肢をクリックしている時の表示>

クリックしてる時の表示も OK だね。

<選択肢をクリックした後の表示>

「クリックされました。」って表示されたってことは、ちゃんとジャンプできたってことだよね。
うん、とりあえずここまではうまくできてるね。
そうだね。
それじゃ、今回はこれくらいにしとこっか。
次回から選択肢を管理するプラグインを作っていくね。
そのプラグインができたらもっと選択肢っぽくなるんだよね?
うん、なると思うよ。
それじゃ、また次回ね!


前へ | TOP | 次へ