8.2 ドラッガブルレイヤを作ってみる

今回からパズルを作っていくわけだけど、 最初はパズルのピースに必要なドラッガブルレイヤから作ってみることにするね。
はーい。
…って言ってもドラッガブルレイヤって結構カンタンに作れるんだけどね。
そーなの?
ドラッグして動かせるようにするのって結構タイヘンそうな気がするんだけど?
それが意外とそうでもないんだ。
まぁ作ってみればわかると思うよ。
ふぅん。
それじゃ作ってみよ。
うん。
まず、ドラッガブルレイヤのクラスを作るわけだけど、 どのクラスを継承して作ればいいかはわかるよね?
ドラッガブルレイヤってゆーくらいだから、Layer クラスなんじゃないの?
ん、正解。
まーさすがにこれはわかるよ。
じゃ最初はいつものようにコンストラクタとデストラクタから作っていくね。
あと、ドラッガブルレイヤのクラスは DraggableLayer クラスって名前にするね。
りょーかい。
これが DraggableLayer クラスのコンストラクタとデストラクタ。

DraggableLayer クラスのコンストラクタとデストラクタ>

class DraggableLayer extends Layer
{
    // コンストラクタ
    function DraggableLayer(win, par)
    {
        // スーパークラスのコンストラクタを呼び出します
        super.Layer(win, par);
    }

    // デストラクタ
    function finalize()
    {
        // スーパークラスのデストラクタを呼び出します
        super.finalize();
    }
}

これって、スーパークラスのコンストラクタとデストラクタを呼び出してるだけ…だよね?
そ。ドラッガブルレイヤには別に特別な初期化処理とか終了処理は必要ないからね。
そーなんだ。
あと、オーバーライドするメソッドは onMouseDownonMouseUponMouseMove の3つだけで、 新しく作るメソッドはゼロ。
えっ、それだけでできちゃうの?
ん、この3つのメソッドだけオーバーライドすれば OK。
じゃまずは onMouseDown メソッドから見ていくね。

onMouseDown メソッド>

// Layer クラスの onMouseDown メソッドをオーバーライドします
function onMouseDown(x, y, button, shift)
{
    // ドラッグが開始されたとみなします
    dragging = true;

    // ドラッグが開始された時の(このレイヤの座標系での)マウスカーソルの座標を保存しておきます
    dragOriginX = x;
    dragOriginY = y;

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

えっと、これって何してるの?
ドラッグする時は、最初にマウスのボタンを押すでしょ。
うん、そーだね。
マウスのボタンが押されると onMouseDown メソッドが呼び出されるから、 ここでドラッグを始める時の準備をしてるんだ。
ドラッグを始める時の準備?
まず、dragging っていう変数に true を代入してるでしょ。
この変数って onMouseDown メソッドの中で宣言されてないから、メンバ変数なのかな?
そうそう。
dragging っていうメンバ変数は、 レイヤがドラッグされてる間だけ true になってて、 それ以外の時は false になってるんだ。
なるほど、名前の通りだね。
onMouseDown メソッドが呼び出されたってことは、 これからレイヤがドラッグされるかもしれないってことだから、 ここで draggingtrue にしてるワケ。
でも、ドラッグされるかもしれないけど、もしかしたらクリックするだけでドラッグしないかもしれないじゃない?
それなのに draggingtrue にしちゃっていいの?
あー、それなら大丈夫。
え、そうなの? なんで?
それは onMouseUp メソッドを見ればわかると思うよ。

onMouseUp メソッド>

function onMouseUp(x, y, button, shift)
{
    // ドラッグが終わったとみなします
    dragging = false;

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

draggingfalse にしてるだけ…?
onMouseUp メソッドはマウスのボタンが離された時に呼び出されるから、 draggingfalse にして、ドラッグが終わったとみなしてるんだ。
そっか。ドラッグしてるのってマウスのボタンを押してる間だけだもんね。
ドラッグせずにマウスのボタンをクリックしただけなら、 onMouseDown メソッドが呼び出されて draggingtrue になった後にすぐ onMouseUp メソッドが呼び出されて draggingfalse に戻るから、結局何にも起こらないワケ。
あ〜、なるほど。だからドラッグするかどうかわからなくても、 とりあえず onMouseDown メソッドで draggingtrue にしても大丈夫なんだね。
そういうこと。
じゃ onMouseDown メソッドの話に戻るね。
次は dragOriginXdragOriginY にマウスカーソルの位置を代入してる部分を見ていくね。
これもメンバ変数っぽいよね?
ん、dragOriginXdragOriginY もメンバ変数だよ。
それぞれドラッグし始めた時のマウスカーソルの x 座標と y 座標を保存しとくために使うの。
図にするとこんな感じ。

onMouseDown メソッドが呼び出された時の状態>

onMouseDown メソッドの引数の xy は、それぞれマウスカーソルとドラッガブルレイヤの左端、上端との距離になってるわけね。
なるほどね。
でもこれって何のために使うの?
ドラッグ中にどれだけマウスカーソルが移動したかを onMouseMove メソッドの中で計算するためだよ。
えっと、どーいうこと?
じゃあ onMouseMove メソッドを見ながら説明するね。
onMouseDown メソッドの方は、 後はスーパークラスの onMouseDown メソッドを呼び出してるだけだから大丈夫だよね?
うん、onMouseDown メソッドの方はおっけーだよ。
ん、それじゃ onMouseMove メソッドを見ていくね。

onMouseMove メソッド>

// Layer クラスの onMouseMove メソッドをオーバーライドします
function onMouseMove(x, y, shift)
{
    if(dragging) // ドラッグ中の場合は…
    {
        // レイヤの移動先の x, y 座標(newLeft, newTop)を計算します
        var newLeft = left + x - dragOriginX;
        var newTop = top + y - dragOriginY;
        // レイヤが親レイヤの範囲外に出ないように位置を調整します
        if(newLeft < -width + 4) newLeft = -width + 4;
        else if(newLeft > parent.width - 4) newLeft = parent.width - 4;
        if(newTop < -height + 4) newTop = -height + 4;
        else if(newTop > parent.height - 4) newTop = parent.height - 4;
        // レイヤの位置を設定します
        setPos(newLeft, newTop);
    }
    // スーパークラスの onMouseMove メソッドを呼び出します
    super.onMouseMove(...);
}

う〜ん、なんかややこしそーだね。
まぁ、とりあえず順番に見ていくことにするね。
そだね。
まず最初の if のとこだけど、これはわかるよね?
if(dragging) ってことは…ドラッグ中だったら draggingtrue になってるから、 if ブロックの中身が実行されるってことだよね?
ん、そうそう。
それじゃ次は if ブロックの中身を見ていくね。
ブロックの中で最初にやってるのが、レイヤの移動先の座標の計算
ここでさっきの dragOriginXdragOriginY が出てきてるね。
…どんな計算をしてるのかはよくわかんないけど。
確かにスクリプトだけだとわかりにくいと思うから、図にしてみるね。
まず、onMouseMove メソッドが呼び出された時に、 マウスカーソルの位置がこんなふうに右下に移動してたとするね。

onMouseMove メソッドが呼び出された時の状態>

onMouseMove メソッドが呼び出された時の xy も、マウスカーソルとレイヤの左端・上端との距離になってるんだ。
えっと、マウスカーソルが右下に移動したってことは、onMouseMove メソッドの xdragOriginX より大きくなってて、 ydragOriginY より大きくなってるってことなんだよね?
ん、そうだよ。
ちなみにマウスカーソルは右方向に (x - dragOriginX) ピクセル、 下方向に (y - dragOriginY) ピクセル移動してるよ。
んー…dragOriginX とマウスカーソルが右方向に移動した距離を足すと x になるから、 移動距離は x から dragOriginX を引いた値になるってことなのかな?
そういうこと。
下方向も同じように考えて、dragOriginY + マウスカーソルが下方向に移動した距離 = y になるから、 下方向の移動距離は (y - dragOriginY) になるわけね。
だね。
あとは、こんなふうにマウスカーソルの位置に合わせてレイヤを移動させれば、 レイヤがドラッグされてるように見えるってワケ。

<マウスカーソルの動きに合わせたドラッガブルレイヤの移動>

うわ、なんかややこしい図だね…
ドラッグする前のドラッガブルレイヤの左端と上端が lefttop になってるから、 ドラッグするためには、ドラッガブルレイヤの左端と上端をそれぞれ newLeftnewTop の位置にすればいいってことはわかる?
うん、それはなんとなくわかるよ。
じゃあとはどうやって newLeftnewTop の値を計算するかだけど、いっぺんに考えるとややこしいから、 まず newLeft の方から考えていくね。
はーい。
leftx を足した長さが newLeftdragOriginX を足した長さとおんなじになってるでしょ?
うん、確かに同じ長さになってるね。
つまり、left + x = newLeft + dragOriginX ってことだから、 この式を変形すると…
newLeft = left + x - dragOriginX、だね。
じゃ newLeft を計算してるスクリプトの方を見てみて。
あっ、おんなじ式になってるね!
ん、あと newTop の方も同じようにすれば計算できるよ。
topy を足した長さが newTopdragOriginY を足した長さとおんなじだから…
top + y = newTop + dragOriginY になって、 この式を変形すると newTop = top + y - dragOriginY だね。
じゃ newTop を計算してるスクリプトの方はどうなってる?
こっちもおんなじになってるね。
newLeftnewTop が計算できたら、 後は setPos メソッドを呼び出してレイヤを移動させれば OK。
setPos メソッドについては §3.2 参照。
なるほどねー。
あ、でも setPos メソッドを呼び出す前になんか if とか else if がいっぱいあるんだけど、これって何してるの?
この if とか else if で、 ドラッガブルレイヤが親レイヤの外に出ちゃわないようにチェックしてるの。
え、それってどーいうこと?
例えば、こんなふうにドラッガブルレイヤをずーっと左に動かすと、 親レイヤ、つまり表画面の背景レイヤの範囲外に出ちゃうんだ。

<ドラッグの過程>

えっと、ドラッガブルレイヤが親レイヤの範囲外に出ちゃうと何か問題あるの?
ドラッガブルレイヤが親レイヤの範囲外に出ると、その部分が表示されなくなるってのは前に確認したよね。
§3.2 参照。
うん、そーだったね。
上の図の(B)の状態だと、ドラッガブルレイヤの一部が表示されてるから特に問題はないんだけど、 さらにドラッグして(C)の状態でドラッグするのをやめると、 ドラッガブルレイヤ全体が表示されなくなるから、二度とドラッグできなくなっちゃうんだ。
あ、そっか。確かに(C)の状態だと画面上にドラッガブルレイヤが表示されてないから、 ドラッガブルレイヤがクリックできなくなっちゃうね。
そうなっちゃうと困るから、 さっきのスクリプトでドラッガブルレイヤが完全に親レイヤの範囲外に出ないようにしてるの。
なるほど。
でも具体的にはどーやってるの?
チェックしてる条件は全部で4つあるんだけど、まずは最初の if(newLeft < -width + 4) newLeft = -width + 4; から見ていくね。
うん。
この if の条件がどういう意味かはわかる?
え〜っと、newLeft-width + 4 より小さい時に真になるんだよね…
でも newLeft-width + 4 より小さい時ってどんな時なんだろ…?
これも図にしてみた方がわかりやすいかな。
これが newLeft-width + 4 の状態だよ。

newLeft-width + 4 の状態>

えっと、これってドラッガブルレイヤの右端が 4 ピクセル分だけ表示されてる状態、ってこと?
そういうこと。
じゃ、newLeft-width + 4 より小さいとどうなると思う?
う〜ん…例えば newLeft-width + 3 だったら、 ドラッガブルレイヤの右端が 3 ピクセル分だけ表示されてる状態ってことだと思うから…
newLeft-width + 4 より小さいと、表示されてる部分が 4 ピクセルより小さくなる、ってことなのかな?
そのとーり。
で、その時は newLeft-width + 4 にしてるわけだから…
ドラッガブルレイヤの右端の表示される部分が 4 ピクセルより小さくなる時は、 4 ピクセル表示されるようにするってこと?
つまり、表示される部分が 4 ピクセルより小さくならないようにしてるってこと。
こうすればどれだけ左にドラッグしても、必ずドラッガブルレイヤの右端は 4 ピクセル以上表示されるでしょ?
なるほど、これなら左にずーっとドラッグしてっても、 ドラッガブルレイヤが完全に親レイヤの範囲外に出ちゃうことはなくなるんだね。
次の条件も基本的に考え方はおんなじ。
まず、newLeft > parent.width - 4 が真になるのは、 右にドラッグしてった時に、ドラッガブルレイヤの左端の表示されてる部分が 4 ピクセル未満になった時だよ。

newLeftparent.width - 4 の時>

parent.width っていうのは親レイヤの幅ってこと?
あそっか。parent プロパティって今回初めて出てくるんだったっけ。
parent プロパティは親レイヤを表すプロパティなんだ。 だから、parent.width は親レイヤの幅だよ。
ちなみにこのドラッガブルレイヤの親レイヤは表画面の背景レイヤだから、 parent.widthkag.fore.base.width と同じ意味になるよ。
えと、じゃあ newLeftparent.width - 4 より大きい時は newLeftparent.width - 4 にしてるから、どれだけ右にドラッグしても、必ずドラッガブルレイヤの左端は 4 ピクセル以上表示される、ってこと?
ん、そういうこと。
あと考え方は同じだから説明は省略させてもらうけど、 残りの2つの条件は、 それぞれドラッガブルレイヤの下端と上端が 4 ピクセル以上表示されるようにするためにチェックしてるんだ。
newLeftnewTop に置き換えて、 あと widthheight に置き換えて考えればいいんだね。
そう。
この4つの条件をチェックすることで、どの方向にどれだけドラッグしても、 ドラッガブルレイヤが完全に親レイヤの範囲外に出ちゃうことはなくなるの。
なるほどね〜。
あ、ところで 4 ピクセルにしてるのってなんでなの?
あぁ、これは別に 4 ピクセルじゃなくても OK だよ。
4 ピクセルくらい表示してればクリックしにくくならないかなと思って 4 ピクセルに設定してるだけ。
そーなんだ。
さてと、これで onMouseMove メソッドは一通りチェックできたわけだけど、 onMouseMove メソッドで何やってるかは大体わかった?
んーと、まず draggingtrue かどうかチェックして、 true だったらドラッグ中ってことだから、newLeftnewTop の値を計算して…
うんうん。
それから、newLeftnewTop の値をチェックして、 親レイヤの範囲外に出てたら、ドラッガブルレイヤの端が 4 ピクセル以上表示されるように値を調整して、 setPos メソッドで位置を設定するんだよね。
ん。で、最後にスーパークラスの onMouseMove メソッドを呼び出して完了、だね。
最初にドラッガブルレイヤは作るのカンタンって言ってたわりにはややこしかったよねぇ。
ん〜、まぁ考え方はちょっとややこしいかもね。
でもスクリプトは短いしそんなに複雑ってほどでもなかったでしょ?
まーそれはそーだけどね。
それじゃこれでドラッガブルレイヤのスクリプトができたから、ちょっと実行してみよっか。
うん、そだね。
first.ks はこんな感じにしとくね。

<first.ks の中身>

; ドラッガブルレイヤ(DraggableLayer クラス)を定義しているファイルを読み込みます
[call storage="DraggableLayer.ks"]

[iscript]
// ドラッガブルレイヤを作って kag オブジェクトに管理してもらいます
kag.add(global.draggableLayer = new DraggableLayer(kag, kag.fore.base));
with(draggableLayer)
{
    // 画像サイズを 128×128 ピクセルに設定します
    .setImageSize(128, 128);
    // レイヤの表示サイズを画像サイズに合わせます
    .setSizeToImageSize();
    // 緑色で塗りつぶします
    .fillRect(0, 0, .width, .height, 0xFF00FF00);
    // 表示状態にします
    .visible = true;
}
[endscript]
add メソッド、fillRect メソッドについては §3.2 参照。
with ステートメント、setSizeToImageSize メソッドについては §3.3 参照。
setImageSize メソッドについては §7.3 参照。

ほとんど TJS スクリプトだね…
まぁ、今回はドラッガブルレイヤを作ってみるだけだからね。
初めて使うメソッドとかは出てきてないと思うけど、スクリプトの内容はわかる?
んと、128×128 ピクセルのドラッガブルレイヤを作って、緑色に塗りつぶして表示してるって感じだよね?
そうそう。
じゃスクリプトはここに置いとくから実行してみて。
わかった。

<実行結果>

表示されたウィンドウ

見た目はやっぱりフツーのレイヤだね。
まぁね。じゃドラッグしてみて。
うん。

<レイヤのドラッグ>

お〜、ちゃんと動かせる〜!
ん、ちゃんとドラッグできてるね。
それじゃちょっとレイヤを画面外にドラッグしてみて。
おっけー。

<画面外へのドラッグ>

ずっと左にドラッグしても、ちゃんとレイヤの端っこが表示されてるね。
ん、これで OK だね。
じゃ今回はここまで。 次回からドラッガブルレイヤを使ってパズルのピースを作っていくことにするね。
は〜い!
それじゃ、また次回ね!


前へ | TOP | 次へ