8.8 パズルを作る(その2)

今回は Puzzle クラスのコンストラクタの続きを見ていくね。
確かまだ半分以上残ってたよね…
そうなんだけど、ここから先は §8.3§8.4§8.5 でやった内容と似てるから。
そーなの?
まぁ前にやった内容よりはちょっと複雑になっちゃうけどね。
う、やっぱり…
ま、とりあえず前回の続きから見ていこ。
そーだね。
今回はまずパズル用の画像の左端と上端の位置をそれぞれ lefttop に代入するとこからね。
elm.left とか elm.top が出てくるってことは、 この位置ってマクロの属性で設定できるってことだよね?
ん、そうだよ。
パズル用の画像の左端と上端はそれぞれ left 属性と top 属性で指定されてる値に設定するの。
ちなみに指定されてなかったら、デフォルトで画面の真ん中になるようにしてるよ。
※三項演算子については §1.20 参照。
指定されてない時って elm.left とか elm.topvoid の時ってことだよね。
そ。あと、画面の真ん中に表示するための計算式は前にも何回か出てきたでしょ?
§3.1§3.5 参照。
baseLayer の幅とか高さから imageLayer の幅とか高さを引いて2で割れば真ん中に表示できるんだよね。
そう。じゃ次ね。
gridColor っていう変数には、ピースの正しい位置を表す枠の色を代入してるの。
ちなみに枠の色はマクロの gridcolor 属性で指定できるよ。
えっと、デフォルトが 0xFF000000 になってるから、 デフォルトだと枠の色は黒色で、あと色は 0xAARRGGBB の形式で指定するってことだよね。
枠を書き込む時に gridColor の値を fillRect メソッドの第5引数に指定するからね。
fillRect メソッドの第5引数は普通 0xAARRGGBB の形式だから。
だね。
で、ここからはピースを作るスクリプトね。
なんかこのへんが一番ややこしそーだよね。
まぁ2次元配列が出てきたりするから、ちょっとややこしいかもねー。
2次元配列?
まず pieceLayers っていうメンバ変数が配列になってるってのはわかるよね?
あ、ちなみに pieceLayers はピース用レイヤのオブジェクトを入れとく配列ね。
※配列(Array クラス)については §1.14 参照。
pieceLayers = new Array(); だから、普通に配列を作ってるだけだよね?
そうそう。
じゃあ、最初の for ブロックの中の1行目を見てみて。
pieceLayers[x] = new Array(); …って、 これ配列の要素にまた配列を作ってるってコト?
そう。
こんなふうに、配列の要素にさらに配列を作ると2次元配列ができるんだ。
2次元配列って普通の配列とどう違うの?
普通の配列はこんな感じでしょ。

<普通の配列(1次元配列)>

うん。普通の配列は変数がつながってるって感じだよね。
2次元配列は、イメージ的にはこんなふうにパズルのピースを並べたような感じなんだ。

<2次元配列>

ホントだ。なんか配列の要素がパズルのピースみたいに並んでるね。
2次元配列だと、要素がパズルのピースと同じように縦方向と横方向に並んでるから、 2次元配列の要素を参照する時は array[0][0] って感じで添え字を2つ付けて、縦方向の位置と横方向の位置をそれぞれ指定するんだ。
array[0] が配列になっているので、 その0番目の要素を参照する時は、普通の配列と同じように後ろに [0] を付けて array[0][0] になる、と考えることもできます。
えと、添え字に x 座標と y 座標を指定するって感じなのかな?
ん、array[y][x] になるね。
あと、2次元配列の要素も普通の配列の要素とおんなじで、値を代入してないと void になるんだ。
あ、だから値を代入してない array[0][1] とかは void なんだね。
そ。あと、配列を作る時にこうやって値を指定しても、上の図とおんなじ配列が作れるよ。

<配列を作る時に値を指定するスクリプト>

var array =
[
 [10,   , 15], // この行の各要素が array[0][0], array[0][1], array[0][2] になります
 [  , 20, 25], // この行の各要素が array[1][0], array[1][1], array[1][2] になります
 [  ,   , 30]  // この行の各要素が array[2][0], array[2][1], array[2][2] になります
];

配列のカッコ([])の中にまた配列のカッコが入っててちょっとややこしい気もするけど…
これって配列の要素がさっきの図とおんなじ並び方になってるね。
あ、でも void のとこには何にも書いてないみたいだけど。
何にも書いてない要素は void になるんだ。
そーなんだ。
ちなみに、3次元以上の配列も作れるんだ。
こんな感じで。

<3次元以上の配列(多次元配列)>

// 3次元配列を作ります
var array3D = new Array();
array3D[0] = new Array();
array3D[0][0] = new Array();
array3D[0][0][0] = 1;
array3D[0][0][1] = 2;

// 4次元配列を作ります
var array4D = new Array();
array4D[0] = new Array();
array4D[0][0] = new Array();
array4D[0][0][0] = new Array();
array4D[0][0][0][0] = "a";
array4D[0][0][0][1] = "b";

な、なんかすごいね…
4次元配列とか要素がどんなふうに並んでるか想像もつかないよ…
まぁ3次元以上の配列を使うことはあんまりないとは思うけどね。
一応こんな配列も作れるってコトで。
だよねぇ…
で、話を戻すと、今回はパズルのピース用レイヤのオブジェクトをこうやって2次元配列のそれぞれの要素に入れとくの。

<パズルのピース用レイヤのオブジェクトを作るスクリプト(コンストラクタより抜粋)>

pieceLayers = new Array(); // ピース用レイヤの配列を作ります
for(var y=0;y<verticalBlocks;y++)
{
    pieceLayers[y] = new Array(); // 2次元配列を作ります
    for(var x=0;x<horizontalBlocks;x++)
    {
        // 左から x 番目、上から y 番目のピース(pieceLayers[y][x])の位置を計算して
        var pieceLeft = left + x * pieceWidth;
        var pieceTop = top + y * pieceHeight;
        // ピース用レイヤを作ります
        pieceLayers[y][x] = new PieceLayer(kag, baseLayer, pieceLeft, pieceTop, onDragStarted, onDragFinished);

        // 以下略
    }
}

え〜っと…最初に pieceLayers を普通の配列にして、 それから最初の for ブロックの中で pieceLayers のそれぞれの要素(pieceLayers[y])をさらに配列にしてるね。
ん、pieceLayers[y] は1行分(horizontalBlocks 個)のピース用レイヤのオブジェクトを入れとく配列になるわけだね。
それから、2番目の for ブロックの中で、 pieceLayers のそれぞれの要素に対してピース用レイヤのオブジェクト(PieceLayer クラスのオブジェクト)を作ってるんだよね。
そうそう。ちなみに pieceLayers[y][x] は左から x 番目で、 上から y 番目にあるピースになってるよ。
ただし、一番左と一番上は 0 番目ってことにしてるからそこは注意してね。
それってつまりピースはさっきの図とおんなじ並び方になってるってことだよね?
そういうこと。
あと、pieceLeftpieceTop はそれぞれピースの左端の位置と上端の位置を表してるんだけど、 どうやって計算してるかわかる?
んと、pieceLayers[y][x] は左から x 番目にあるから、 一番左を 0 番目って数えると、このピースより左側には x 個のピースがあるってことになるよね。
うんうん。
つまり、ピースの左端の位置は『画像の左端の位置+このピースより左にあるピースの数×ピースの幅』ってことになるから left + x * pieceWidth で計算できるんだよね。
ん、そのとーり。
じゃ上端の方は?
上端の方もおんなじ考え方で、『画像の上端の位置+このピースより上にあるピースの数×ピースの高さ』だから pieceTop = top + y * pieceHeight になるよね。
うん、バッチリだね。
でしょ〜。こーいう位置の計算ってだいぶ分かるようになってきたんだ。
確かに位置の計算って今までにも結構やってるからね〜。
ちょっとややこしいんだけど、一応図にしとくとこんな感じだね。

<各ピースの位置の計算>

じゃあ次はピース用レイヤの設定をやってる部分を見ていくね。

<ピース用レイヤの設定部(コンストラクタより抜粋)>

with(pieceLayers[y][x])
{
    .setImageSize(pieceWidth, pieceHeight); // レイヤの画像サイズを pieceWidth×pieceHeight に設定します
    .setSizeToImageSize(); // レイヤの表示サイズを画像サイズに合わせます
    // このピースに表示する画像を完成画像からコピーします
    .copyRect(0, 0, imageLayer, x * pieceWidth, y * pieceHeight, pieceWidth, pieceHeight);
    // 位置をランダムに設定します
    .setPos(int (Math.random() * (kag.fore.base.width - pieceWidth)), int (Math.random() * (kag.fore.base.height - pieceHeight)));
    .absolute = x + y * horizontalBlocks + 1; // 重ね合わせ順序を設定します
    .visible = true// 表示状態にします
}

このスクリプトは最初にパズルのピースを作った時とほぼおんなじだから、違ってるとこだけ説明するね。
§8.3§8.5 参照。
りょ〜かい。
ちなみにどこが違ってるかわかる?
えっと…前は copyRect メソッドじゃなくて fillRect メソッドを使ってて…
あと、absolute プロパティは設定してなかったよね。
ん、そうだね。
なんで今回は copyRect メソッドを使ってるの?
それぞれのピースには完成画像の一部が表示されてるよね。
うん。
完成画像は pieceLayer に読み込まれてるから、 copyRect メソッドを使ってこんなふうに pieceLayer の一部をコピーしてピースを作るの。
copyRect メソッドについては §4.4 参照。

<ピース画像の作成>

例えば、左上に表示するピースは pieceLayers[0][0] だから、 pieceLayers[0][0].copyRect(0, 0, imageLayer, 0, 0, pieceWidth, pieceHeight); を実行すれば左上のピースに表示する画像をコピーできるわけね。
左から x 番目、上から y 番目にあるピースは imageLayer(x * pieceWidth, y * pieceHeight) の位置から pieceWidth×pieceHeight ピクセルの部分をコピーすればいいから、 copyRect(0, 0, imageLayer, x * pieceWidth, y * pieceHeight, pieceWidth, pieceHeight) になるんだね。
ん、そういうこと。
それじゃ、absolute の設定の方はわかる?
ん〜っと、pieceLayers[y][x].absolute = x + y * horizontalBlocks + 1; だから…

<各ピース用レイヤの absolute の値>

例えば、horizontalBlocksverticalBlocks がどっちとも 3 だったら、 左上のレイヤ(pieceLayers[0][0])の absolute1 で、 右下にいくほど値が大きくなっていって、右下のレイヤ(pieceLayers[2][2])の absolute9 になるんじゃないかな?
図にするとこんな感じだよね。
ん、そうなるね。
じゃこれでピース用レイヤの設定は OK かな?
うん。
じゃ次はピースの正しい位置を表す枠を描いてる部分ね。
…って言ってもコレは前に枠を描いた時とおんなじやり方だから大丈夫だと思うんだけど、どうかな?
う〜ん、確かに引数はよく似てるよね。
前は goalLeft だったのが pieceLeft になってたり、 第5引数が gridColor になってたりするけど。
pieceLeftgoalLeft と同じでピースの正しい位置を表す値になってるし、 gridColor はマクロの gridcolor 属性で指定されてる値(デフォルトだと0xFF000000)に置き換わるだけだよ。
だね。
うん、これはだいじょぶだよ。
ん、じゃいくね。
これも fillRect メソッドが4回呼び出されてるから、枠を描いてる…んだよね?
そうだよ。
でもこれってドコに枠を描いてるの?
このスクリプトでそれぞれのピースの周りに枠を描くだけだと、 こんな感じに外側の枠だけが細くなっちゃうんだ。

<ピースの周りだけに枠を描いた場合>

確かに周りの線だけ他の線よりちょっと細くなってるね。
だから、全体の線の太さを揃えるために、 このスクリプトで周りの線(下の図の青い部分)を描いてるの。

なるほどねぇ…
ちなみに、周りの線はピースの外側に描いてるから、 最初の fillRect メソッドの第1・第2引数はそれぞれ left - 1top - 1 になってて、 線の長さと高さは width + 2height + 2 になってるわけね。 こんなふうに。

なんかややこしい図だけど、まぁ何となく解るかな。
で、最後に右クリックでギブアップできるように、 右クリックフックを登録して終わり。
右クリックフック…ってなに?
右クリックフックっていうのは、画面上で右クリックした時に予め登録したメソッドが呼び出されるようにする機能のことだよ。
パズルを途中で止めたくなったり、最初からやり直したりしたくなった時に、 右クリックすることでギブアップできるようにするために右クリックフックの機能を使うの。
えっと、それってつまり右クリックサブルーチンを使うってコト?
んーん、右クリックフックと右クリックサブルーチンは別モノだよ。
え? じゃあ右クリックフックと右クリックサブルーチンってどう違うの?
ん〜、これからフックの話をするとだいぶ長くなっちゃいそうだから、次回改めて詳しく説明することにするね。
あ、そーなの?
うん。
ってワケで、続きは次回ね!


前へ | TOP | 次へ