8.5 パズルのピースを作る(その3)

前回はピース用のレイヤオブジェクト(PieceLayer クラスのオブジェクト)を作るとこまでチェックできたから、 今回はドラッグが始まった時に呼び出される onDragStarted メソッドから見ていくね。
このメソッド にも fillRect メソッドが出てきてるね。
ホントはドラッグが始まった時はドラッグするピースを一番手前に表示するんだけど、 今は1個しかピースを作ってないから、とりあえずドラッグしてる時はピースの色を変えることにしてみたんだ。
なるほど、1個しかないんじゃ重ね合わせ順序の変えようがないもんね。
えっと、引数の piece がドラッグするピースのオブジェクトへの参照になってるんだよね?
そ。つまり pieceglobal.pieceLayer とおんなじってことだね。
第1引数と第2引数がどっちも 0 で、 第3引数と第4引数はそれぞれレイヤの幅と高さになってて、 第5引数は 0xFFFF0000 だから…全体を赤色で塗りつぶしてるってことかな?
前に実行した時に、ピースの上でマウスのボタンを押したらピースが赤くなったでしょ。
§8.3 参照。
うん、そーだったね。
じゃ次はドラッグが終わった時に呼び出されるメソッド
なんかこっちの方はややこしそーだね…
最初の fillRect メソッドはさっきとおんなじやり方だからわかるよね?
こっちのは第5引数が 0xFF0000FF になってるから、 今度は青色に塗りつぶしてるんだよね。
ん、だからピースの色は普段は青色で、ドラッグしてる間だけ赤色になるわけね。
だね。
その後のスクリプトで、ピースが正しい位置におかれてるかどうかチェックしてるんだけど、 まず1つ目の if の条件はどうなってるかわかる?
with(piece) のブロックの中だから、 .leftpiece.left って意味で、 .goalLeft.goalTop はそれぞれ piece.goalLeftpiece.goalTop って意味になるよね?
だから .left はピースの左端の位置で、 .goalLeft.goalTop はピースの正しい位置、 つまり PieceLayer クラスのオブジェクトを作る時に、 第3引数と第4引数に指定した goalLeftgoalTop の値ってことになるね。
んーと、じゃあ .left >= .goalLeft - 2 ってのは、 ピースの左端の位置が goalLeft - 2 以上ってことで、 .left <= .goalLeft + 2 はピースの左端の位置が goalLeft + 2 以上ってことだから…
その2つの式が && でつながってるってことは?
ピースの左端の位置が goalLeft - 2 から goalLeft + 2 の間にあれば条件が真になって、 そうじゃなかったら偽になるよね。
そ。言い換えると、2ピクセルまでだったらピースの左端の位置が正しい位置からずれてても真になるってこと。

<ピースの横方向のずれの許容範囲>

それじゃあ、2ピクセル以内だったらずれてても正しい位置に置かれたって見なしてるってこと?
そういうこと。
で、1つ目の if の条件が真になった時、 つまり left が正しい位置の範囲内にある時は、 次に2つ目の if の条件をチェックするの。
今度はさっき left だったとこが top に変わってるから、 ピースの上端の位置が goalTop - 2 以上で goalTop + 2 以下だったら真になるね。
つまり、2ピクセルまでだったらピースの上端の位置が正しい位置からずれてても OK ってことだね。

<ピースの縦方向のずれの許容範囲>

結局、下の図の 斜線 の領域の中にピースが置かれたら、 この2つの if の条件が真になるワケ。

<ピースのずれの許容範囲>

じゃあピースはこの領域の中だったらどこに置いても正しい位置に置いたことになるんだね。
で、その時は if ブロックの中身が実行されるわけだね。
最初に setPos メソッドでピースの位置を (.goalLeft, .goalTop) に設定してるみたいだけど、 これって何のためにやってるの?
ピースは±2ピクセルまでだったらずれててもいいわけだから、 必ずしもぴったり正しい位置に置かれるとは限らないわけでしょ?
うん、まーそーだよね。
今回は別にぴったり正しい位置に置かなくてもそんなに問題はないけど、 ちゃんとしたパズルを作る時は、正しい位置に移動しとかないとパズルが完成した時に絵の一部がずれちゃうからね。
あーなるほどね。
じゃあこのスクリプトってちゃんとしたパズルを作ることを考えて作ってたの?
ん、このスクリプトはこの後パズルを作る時にそのまま使うつもりだから。
あ、そーだったんだ。
で、ピースの位置を合わせたら、次は enabled プロパティを false にして、それ以上動かせないようにするの。
動かせなくしちゃっていいの?
正しい位置に置いたピースをわざわざ別の位置に動かす必要は普通ないと思うし、 それに動かせなくしなかったら、 せっかく正しい位置に置いたピースを間違って他所に動かしちゃったりするかもしれないしね。
なるほどねぇ。
えっと、enabled プロパティって前にボタンを作った時に出てきたよね。
確か false にするとボタンが押せなくなるんだったと思うんだけど?
§5.4 参照。
そうそう。
今回の場合は enabled プロパティを false に設定することで、 onMouseDown メソッドと onMouseUp メソッドと onMouseMove メソッドが呼び出されないようにしてるんだ。
onMouseDown メソッドと onMouseUp メソッドが呼び出されないってことは、 onDragStarted メソッドと onDragFinished メソッドも呼び出されないってことだよね?
もちろん。
あとレイヤをドラッグしてる時は DraggableLayer クラスの onMouseMove メソッドの中でレイヤを移動させてるから、 onMouseMove メソッドが呼び出されないってことはレイヤをドラッグできないってことになるね。
確かにそーなるね。
最後に、inform メソッドでメッセージを表示して終わり。
そーいえばピースを正しい位置に置いた時に「OK」っていうメッセージが表示されたね。
じゃこれで onDragFinished メソッドは一通りチェックできたから、 次はピース用レイヤの設定をやってるスクリプトを見ていくね。
最初は setImageSize メソッドでレイヤの画像サイズを piceWidth×pieceHeight ピクセルに設定してるね。
setImageSize メソッドについては §7.3 参照。
それから setSizeToImageSize メソッドでレイヤの表示サイズを画像サイズに合わせて、 fillRect メソッドで青色に塗りつぶしてるの。
それって onDragFinished メソッドでもやってたよね?
onDragFinished メソッドはドラッグが終わった時にしか呼び出されないから、 ここで fillRect メソッドを呼び出しとかないと、最初に表示した時に青色にならないでしょ。
あ、確かにそーだね。
で、次に setPos メソッドでピースを適当な位置に置いてるの。
なんかすごい式がややこしーんだけど…
しかも Math.random ってゆー見たことないメソッドが使ってあるし…
じゃまず Math クラスの random メソッドから説明するね。
うん。
Math クラスっていうのは、名前の通り数学的な計算のために使うクラスで、 random メソッドは 0 以上 1 未満の乱数を取得するためのメソッドなんだ。
ちなみに Math クラスは TJS に元々組み込まれてるクラスだから、定義しなくても使えるよ。
※詳しくは TJS2 リファレンスの「Math クラス」の項目を参照してください。
0 以上 1 未満の乱数?
乱数っていうのはランダムな数値のことだよ。
だから、Math.random メソッドは呼び出すたびに違う値を返すの。
つまり、どんな値が返ってくるかは呼び出してみるまでわからないってこと。
うーん、なんとなくわかったようなわかんないような…?
例えば、サイコロって振ってみるまではどんな目が出るかわからないよね?
うん、そだね。
それと同じことが Math.random メソッドを使うとできるの。
そーなの?
ん、こんな感じにね。

<サイコロを振るのと同じ動作をするスクリプト>

System.inform(int (Math.random() * 6) + 1);

このスクリプトを実行すると、「1」〜「6」のどれかの数字がランダムに表示されるよ。
first.ks とかの KAG スクリプトから実行する時はこう書いてね。

<サイコロを振るのと同じ動作をするスクリプト(KAG スクリプトの中で実行する場合)>

[eval exp="System.inform(int (Math.random() * 6) + 1)"]

ちょっと実行してみて。
うん、わかった。
実行中…
ホントだ。1〜6 の間で毎回違う数字が表示されるね。
Math.random は 0 以上 1 未満の数を返すから、 Math.random() * 6 は何以上何未満の数になると思う?
え? えっと、6倍してるんだから…
0 以上 6 未満になるんじゃない?
ん、そうだね。
じゃあ int (Math.random() * 6) はどうなる?
int ってなんだったっけ?
int 演算子は、右側にある値を整数値に変換する演算子だよ。
小数部分は切り捨てられるから、例えば int 1.21 になるよ。
int 演算子については §1.6 参照。
う〜ん、じゃあ 0 以上 6 未満の数を整数値に変換するってことだよね…
0 以上 6 未満の整数にどんな数があるかを考えればわかると思うよ。
0 以上 6 未満の整数…ってことは、0 と 1 と 2 と 3 と 4 と 5 ってこと?
そ。じゃ int (Math.random() * 6) + 1 はどんな数になる?
1 を足すんだから、1, 2, 3, 4, 5, 6 だね。
ちゃんとサイコロの目の数字になってるでしょ?
うん、なってるね。
んじゃピース用レイヤの位置設定のスクリプトに戻るね。
setPos メソッドの第1引数は int (Math.random() * (kag.fore.base.width - .width)) だから…
0 以上 (kag.fore.base.width - .width) 未満の整数になるってことだよね?
そうそう。
背景レイヤの幅が出てくるのはなんとなくわかるけど、何でピース用レイヤの幅を引いてるの?
もし int (Math.random() * kag.fore.base.width) にすると、 例えば random メソッドが 0.9999 っていう値を返した時には、 int (Math.random() * kag.fore.base.width)(kag.fore.base.width - 1) になるよね。
えっと、背景レイヤの幅が 640 ピクセルだとすると、640×0.9999=639.936 で、 小数部分は切り捨てられるから、確かに kag.fore.base.width - 1 (=639) になるね。
ピース用レイヤの左端が (kag.fore.base.width - 1) になると、 こんなふうにピースがほんのちょっとしか表示されなくなっちゃうから、ドラッグしにくくなっちゃうでしょ。

<ピース用レイヤの左端が (kag.fore.base.width - 1) の場合>

確かにこれだとドラッグしにくいし、ピースがどこにあるかもわかりにくくなっちゃってるね。
int (Math.random() * (kag.fore.base.width - .width)) にすると、 左端は最高でも (kag.fore.base.width - .width - 1) にしかならないから、 こんなふうにピース全体が確実に表示されるんだ。

<ピース用レイヤの左端が (kag.fore.base.width - .width - 1) の場合(最も右に表示されている場合)>

なるほどねー。
じゃあ第2引数を int (Math.random() * (kag.fore.base.height - .height)) にしてるのもおんなじ理由?
ん、ピース用レイヤの高さを引いとけば、 一番下に表示される場合でも、レイヤの上端は (kag.fore.base.height - .height - 1) だから、 こんなふうにピース全体を確実に表示できるよね。

<ピース用レイヤの上端が (kag.fore.base.height - .height - 1) の場合(最も下に表示されている場合)>

えっと、じゃあ setPos メソッドを呼び出した後は、 ピースがランダムな位置に表示されるけど、必ずピース全体は表示されるってことなんだね。
そう。
後は visibletrue にしてるだけだから大丈夫だよね?
うん、おっけーだよ。
これで first.ks でやってることはわかった?
うん、大体はね。
それじゃ、今回はここまで。
次回からこのピースを使ってパズルを作っていくね。
それじゃ、また次回!


前へ | TOP | 次へ