8.9 パズルを作る(その3)

今回は右クリックフックについて詳しく見ていくね。
右クリックフックって、確か右クリックした時に何かのメソッドを呼び出すようにする機能、みたいな感じだったよね?
そうそう。
なんかそれって右クリックサブルーチンと似てる気がするんだけど、 右クリックフックって右クリックサブルーチンとどう違うの?
まず、右クリックサブルーチンは右クリックした時に指定したサブルーチンを呼び出したり、 指定したラベルにジャンプしたりするけど、 右クリックフックは右クリックした時に指定したメソッドを呼び出すだけで、 ラベルにジャンプしたりとかはしないんだ。
ふぅん、そーなんだ。
あと、右クリックサブルーチンだと、右クリックした時に呼び出せるサブルーチンは1つだけだけど、 右クリックフックだと、右クリックした時に複数のメソッドを呼び出せるの。
じゃあ今回右クリックフックを使ったのって、サブルーチンを呼び出したりラベルにジャンプしたりしたくなかったからなの?
それとも複数のメソッドを呼び出したかったから?
いやー、別に今回は右クリックサブルーチンを使っても良かったんだけどね。
えっ、そーなの?
ただ、右クリックフックを使った方が便利な時もあるし、 右クリックだけじゃなくて、左クリックとか他のフックもあるから、 フックの機能は知っといて損はないかなーと思って使うことにしたの。
なるほどねぇ…
それじゃ実際に右クリックフックを使ってみることにするね。
例えば…

<右クリックフックの例1(first.ks)>

[iscript]
// 右クリックされた時に呼び出されるメソッド
function onRightClick()
{
    System.inform("右クリックされました。");
    return true;
}
// 右クリックフックを登録します
kag.rightClickHook.add(onRightClick);
[endscript]

右クリックしてください。

このスクリプトを first.ks に書き込んで実行してみて。
おっけー。

<実行結果>

右クリックすればいーんだよね?
ん。

<右クリックした結果>

あっ、メッセージボックスが表示された。
ん、これで OK。
それじゃスクリプトの方を見てこっか。
onRightClick っていうメソッドの中で inform メソッドを呼び出して「右クリックされました。」っていうメッセージボックスを表示してるから…
右クリックした時にこのメソッドが呼び出されたってコトだよね?
そうだよ。
onRightClick メソッドの定義の後に kag.rightClickHook.add(onRightClick); っていう部分があるでしょ。
これで右クリックフックに onRightClick メソッドを登録してるんだ。
えっと、じゃあ右クリックフックに onRightClick メソッドを登録した後は、 右クリックすると onRightClick メソッドが呼び出されるようになるってこと?
そういうこと。じゃ今度は…

<右クリックフックの例2(first.ks)>

[iscript]
// 右クリックされた時に呼び出されるメソッド
function onRightClick()
{
    System.inform("右クリックされました。");
    return false;
}
// 右クリックフックを登録します
kag.rightClickHook.add(onRightClick);
[endscript]

右クリックしてください。

このスクリプトを first.ks に書き込んで実行してみて。
なんかさっきのスクリプトとおんなじに見えるんだけど…
あ、今度は onRightClick メソッドが false を返すようになってるんだね。
ん、さっきと違ってるのはそこだけだよ。
それじゃあ実行してみるね。

<実行して右クリックした結果>

右クリックしたけど、さっきとおんなじだよ?
OK ボタンを押してみて。
う、うん。

<OK ボタンを押した後の状態>

あっ、メッセージウィンドウに表示されてた文字が消えた!
正確には、メッセージウィンドウに表示されてた文字が消えたんじゃなくて、 メッセージウィンドウが非表示になったの。
あ、そーなんだ。
でもなんで?
右クリックした時のデフォルトの動作ってなんだった?
メッセージウィンドウを非表示にしたり表示したりするんだよね?
あ、もしかして右クリックした時のデフォルトの動作が実行されたからメッセージウィンドウが非表示になったの?
そのとーり。
右クリックフックに登録したメソッドが true を返すと右クリックのデフォルトの動作は実行されないんだけど、 false を返すと実行されるようになってるんだ。
へぇ、そうなんだ。
あと、kag.rightClickHook は配列になってるから…
※配列については §1.14 参照。

<右クリックフックの例3(first.ks)>

[iscript]
// 右クリックされた時に最初に呼び出されるメソッド
function onRightClick1()
{
    System.inform("右クリックされました。");
    return true;
}
// 右クリックされた時に2番目に呼び出されるメソッド
function onRightClick2()
{
    kag.process("""*rclick");
    return true;
}
// 右クリックフックを登録します
kag.rightClickHook.add(onRightClick1);
kag.rightClickHook.add(onRightClick2);
[endscript]

右クリックしてください。
[s]

*rclick
[r]
右クリックされました。

こんなふうに、2つ以上のメソッドを登録することもできるんだ。
onRightClick1onRightClick2 っていうメソッドを両方登録してるんだね。
メソッドを複数登録すると、先に登録したメソッドから順に呼び出されるから、 この場合は最初に onRightClick1 メソッドが呼び出されて、 それから onRightClick2 メソッドが呼び出されるの。
試しに実行してみて。
うん、わかった。

<実行して右クリックした状態>

<OK ボタンを押した後の状態>

メッセージウィンドウにも「右クリックされました。」って表示されたね。
えっと、これって onRightClick1 メソッドが呼び出された後に onRightClick2 メソッドが呼び出されて、 *rclick っていうラベルにジャンプしたからなんだよね?
kag.process メソッドについては §7.4 参照。
ん、そうだよ。
ちなみに、メソッドを複数登録した場合は、全部のメソッドが false を返した時だけ右クリックのデフォルトの動作が実行されるんだ。
それじゃあ、1つでも true を返すメソッドがあったら、 デフォルトの動作は実行されないってこと?
そ。
あと、右クリックだけじゃなくって、左クリックにもフックがあるんだ。
そーいえば最初にそんなこと言ってたよね。
左クリックフックには kag.leftClickHook.add(メソッドへの参照); で左クリックした時に実行するメソッドを登録できるんだ。
例えばこんな感じ。

<左クリックフックの例(first.ks)>

[iscript]
// 左クリックされた時に呼び出されるメソッド
function onLeftClick()
{
    System.inform("左クリックされました。");
    return true;
}
// 左クリックフックを登録します
kag.leftClickHook.add(onLeftClick);
[endscript]

左クリックしてください。[p][cm]
左クリックされました。

なんか右クリックフックの時とスクリプトが似てるね。
左クリックフックの使い方は右クリックフックと同じだからね。
このスクリプトを first.ks に書き込んで実行して、左クリックすると…

<実行して左クリックした状態>

こんなふうに「左クリックされました。」っていうメッセージボックスが表示されるの。
※左クリックの他に Enter キーやスペースキーを押した場合も、 左クリックフックに登録したメソッドが呼び出されます。
右クリックフックの時とおんなじだね。
じゃあ、左クリックフックに登録してる onLeftClick メソッドtrue を返した時と false を返した時で、 OK ボタンを押した後にどんな違いが出ると思う?
えっ? え〜っと…
false を返すと左クリックのデフォルトの動作が実行されるけど、 true を返すと実行されないんだよね?
そだね。それは右クリックフックの時とおんなじ。
ふつーは左クリックで次のメッセージを表示するから…左クリックのデフォルトの動作って、 次のメッセージを表示するコトなのかな?
左クリックすると、普通は p タグや l タグのクリック待ちから抜けて、 シナリオの実行が再開されるよね。
だから、このスクリプトの場合は p タグの次のメッセージが表示されるわけだね。
じゃあ、onLeftClick メソッドtrue を返したら、OK ボタンを押すとメッセージボックスが消えるだけだけど、 false を返したら、 OK ボタンを押した後に次の「左クリックされました。」っていうメッセージが表示されるんじゃないかな?
ん、そのとーり。
あと、左クリックフックにも複数のメソッドを登録できるから。
左クリックの場合もやっぱり全部のメソッドが false を返した時だけ左クリックのデフォルトの動作が実行されるの?
ん、そうだよ。
じゃこれで左クリックフックも OK かな。
うん。
フックにはあともう1種類あるんだけど、どんなフックかわかる?
え、まだあるの?
う〜ん、なんだろ…ダブルクリック、とか?
残念ながらダブルクリックにはフックは無いんだ。
ダブルクリックって KAG では普通使わないし、デフォルトの動作も無いからね。
あ、そーいえばそーだね…じゃあもう1つのフックってなんなの?
キーが押された時に登録されたメソッドを呼び出すキーダウン(keydown)フックだよ。
あー、なるほど。キーが押された時ね。
キーダウンフックの使い方は基本的に右クリックフックや左クリックフックと同じなんだけど、 ちょっと違ってる部分もあるんだ。
どう違ってるの?
クリックのフックは右と左に分かれてるから、マウスのどっち側のボタンが押されたのか判るけど、 キーダウンフックはキーごとに分かれてるわけじゃないから、 どのキーが押されたのかが判るようにしてあるの。
そっか。キーはいっぱいあるからキーごとにフックを作るのは大変だよね。
…ってことは、onKeyDown メソッドみたいに、 どのキーが押されたのかが引数になってるとか?
そう。キーダウンフックには引数が2つあって、第1引数には押されたキーの仮想キーコード、 第2引数には同時に押されてたシフト系のキーの状態がセットされてるんだ。
つまり、Layer クラスの onKeyDown メソッドの第1・第2引数と同じなわけね。
Layer クラスの onKeyDown メソッドについては §3.11 参照。
仮想キーコードって前に出てきたことあったと思うけど、どんなのだったっけ?
仮想キーコードはそれぞれのキーを表す値のことで、 例えば A キーだと VK_A っていう値が割り当てられてるの。
だから、第1引数の値が VK_A になってたら、A キーが押されたって判るわけね。
※仮想キーコードについては §3.11 または「吉里吉里2リファレンス」−「付録:仮想キーコード一覧」参照。
そーいえばなんか if(key == VK_A) みたいなチェックをやったね。
だね。
で、キーダウンフックの使い方はこんな感じ。

<キーダウンフックの例1(first.ks)>

[iscript]
// キーが押された時に呼び出されるメソッド
function onKeyDown(key, shift)
{
    if(key >= VK_A && key <= VK_Z)
        System.inform($key + "キーが押されました。");
    return true;
}
// キーダウンフックを登録します
kag.keyDownHook.add(onKeyDown);
[endscript]

アルファベットのキーを押してください。

このスクリプトを first.ks に書き込んで実行すると、アルファベットのキー(Aキー〜Zキー)を押した時だけ、 どのキーが押されたかがメッセージボックスに表示されるの。
押されたキーがアルファベットのキーかどうかをチェックしてるのが if(key >= VK_A && key <= VK_Z) ってトコ?
そうだよ。
アルファベットの仮想キーコードは ABC 順に並んでるから、仮想キーコードの値が VK_A 以上 VK_Z 以下なら A〜Z のどれかのキーが押されたってことになるの。
なるほどねぇ。
あ、あと inform メソッドの引数の中にある $key ってなんなの?
この $ は単項演算子の一種で、 右側にある値を仮想キーコード(厳密には文字コードなんだけどね)と見なして、 その値に対応した文字列が演算子全体の値になるの。
※単項演算子については §1.6 参照。
えっと…どーゆーこと?
例えば、key の値が VK_A だったら、 $key の値は "A" っていう文字列になるってこと。
つまり、A キーが押された時は、inform メソッドの引数が 「"A" + "キーが押されました。"」になるから、 「Aキーが押されました。」っていうメッセージが表示されるわけね。
ふぅん、そーなんだ。
ところで、A キーのデフォルトの動作って何だった?
確か A キーを押すとオートモードになるんだったよね?
そうだね。だから、キーダウンフックに登録したメソッドが true を返すと…
デフォルトの動作が実行されなくなるから、 A キーを押してもオートモードにならなくなるんだね。
ん、そういうこと。
キーダウンフックはこんなとこかな。
あ、えっと、第2引数の shift ってどうやって使うのかちょっと気になるんだけど…
そういえば shift の方は説明してなかったね。
ん〜、でも shift の方はちょっとややこしいから、カンタンに使い方を説明しとくね。
そーなんだ。うん、わかった。
例えば、アルファベットのキーと一緒に Ctrl キーが押されてる時だけメッセージボックスを表示する時はこんな感じ。

<キーダウンフックの例2(first.ks)>

[iscript]
// キーが押された時に呼び出されるメソッド
function onKeyDown(key, shift)
{
    // アルファベットのキーと一緒に Ctrl キーが押されている時だけメッセージを表示します
    if((key >= VK_A && key <= VK_Z) && (shift & ssCtrl))
        System.inform("Ctrl+" + $key + "キーが押されました。");
    return true;
}
// キーダウンフックを登録します
kag.keyDownHook.add(onKeyDown);
[endscript]

Ctrlキーと一緒にアルファベットのキーを押してください。

なんか if の条件のとこに (shift & ssCtrl) ってゆーのが増えてるね。
(shift & ssCtrl) は、 Ctrl キーが押されてる時は真になって、押されてない時は偽になるんだ。
ってことは、アルファベットのキーが押されてて、さらに Ctrl キーが押されてると if の条件が真になってメッセージが表示されるってことだよね?
そうそう。
あ、ちょっと思ったんだけど、(shift & ssCtrl) じゃなくって (shift && ssCtrl) なんじゃない?
んーん、(shift & ssCtrl) だよ。
え、そうなの?
詳しい説明は省略させてもらうけど、& はビット AND 演算子って言って、 && (論理 AND 演算子)とは違う演算子なんだ。 (shift && ssCtrl) って書くと Ctrl キーが押されてない時にも真になっちゃうことがあるから注意してね。
へぇ、そーなんだ。
ちなみに、(shift & ssShift)Shift キーが押されてる時に真になって、 (shift & ssAlt)Alt キーが押されてる時に真になるよ。
結構色々あるんだね。
他にもいくつかあるから、 詳しく知りたい時は「吉里吉里2リファレンス」の「Layer クラス − onKeyDown メソッド」の項目とかを見てみてね。
りょーかい。
それじゃ、改めて Puzzle クラスのコンストラクタの右クリックフックを登録してるとこを見てみるね。
やっと Puzzle クラスのコンストラクタに戻ってきたね。
確かにフックの話がだいぶ長くなっちゃったからね。
えっと、kag.rightClickHook.add(onGiveUp); だから、 右クリックした時に onGiveUp っていうメソッドが呼び出されるようにしてるんだよね。
そうそう。
onGiveUp メソッドはまた後で見ていくことにするね。
うん、わかった。
んじゃ次は Puzzle クラスのデストラクタを見てくね。

Puzzle クラスのデストラクタ>

// デストラクタ
function finalize()
{
    // 右クリックフックの登録を解除します
    kag.rightClickHook.remove(onGiveUp);

    // すべてのピース用レイヤを無効化します
    for(var y=0;y<horizontalBlocks;y++)
    {
        for(var x=0;x<verticalBlocks;x++)
            invalidate pieceLayers[y][x];
    }
    invalidate imageLayer; // 完成画像を読み込んでおくレイヤを無効化します
    invalidate baseLayer; // 背景用レイヤを無効化します
}

kag.rightClickHook.remove(onGiveUp); って書いてあるけど、これって?
Array クラスの remove メソッドは覚えてる?
え? え〜っと…どんなメソッドだったっけ?
remove メソッドは、引数に指定した要素を配列から削除するメソッドだよ。
つまり、ここで remove メソッドを呼び出して onGiveUp メソッドへの参照を kag.rightClickHook から削除することで onGiveUp メソッドの登録を解除してるわけね。
Array クラスの remove メソッドについては §1.14 参照。
登録を解除したら、その後は右クリックしても onGiveUp メソッドが呼び出されなくなるってコト?
そう。デストラクタが実行されたら、もうそのオブジェクトは使えなくなって、 onGiveUp メソッドも呼び出せなくなっちゃうから、 ここで登録を解除しとかないといけないの。
なるほどねぇ。
後はパズルで使ってるレイヤを無効化してるだけだから大丈夫だと思うんだけど、どうかな?
2次元配列の無効化がちょっとややこしい感じだけど、 これってピース用レイヤのオブジェクトを作った時と同じように for ループを作って無効化していけばいいんだよね?
ん、そうそう。
さて、それじゃ今回はこれくらいにしとこっか。
そだね。
次回は Puzzle クラスのメソッドを見ていくことにするね。
それじゃ、また次回!


前へ | TOP | 次へ