コラム / テトリスぽいもの / 13



interface>type
に、クラス宣言を追加します。
   TPiece = Class(TObject)
   private
   public
       x,y:integer;
       d_counter,d_max:integer;
       shape:Array[0..3,0..3] of Boolean;
       MapBlocks:TMapBlocks;
       procedure Draw;
       function isHit(vx,vy:integer):Boolean;
       procedure CopySelfToMapBlocks;
   end;
注意:interfaceの中では、自分より後に定義されたクラスは参照できません。
なので、TPieceはTMapBlocksより下で定義してください。

img5.png

変数の説明です。
x,y
座標。ちなみにshape[0,0]がある位置。
d_counter,d_max
落下(Down)カウンタと、落下するカウンタ量。
d_maxが小さいほど早く落ちていく。
shape

shape.png

こういうデータを持つための4*4の二次元配列。
第一要素がy、第二要素がx。
逆の方がわかりやすいが、フィールドクラスで間違えたため統一・・・
MapBlocks
フィールドクラスへの参照。
で、手続き、関数の説明
Draw
自分を描画する処理。 
isHit
vx,vyだけ移動したら、フィールド上の既存ブロックに接触するかを
返す関数。真ならその方向には行けないことになる。
下にいけなくなったら止まらなければならない。
CopySelfToMapBlocks
自分のブロックを、フィールド情報にコピーする。
止まるときに実行し、その後自分自身は消える。
まずはDrawの実装部
procedure TPiece.Draw;
var i,j:integer;
begin
   For i:=0 to 3 do begin
       For j:=0 to 3 do begin
           if shape[i,j] then begin
               Form1.CopyBlockImageToField(x+j,y+i,Form1.b_img);
           end;
       end;
   end;
end;
TMapBlocksとほぼ同じ。
全マスについて以下を行っていて、
           if shape[i,j] then begin
               Form1.CopyBlockImageToField(x+j,y+i,Form1.b_img);
           end;
shape[i,j]が真
つまり、自分の形状でブロックがある部分なら、
               Form1.CopyBlockImageToField(x+j,y+i,Form1.b_img);
描画する。
その時、shape[0,0]の座標はx,yで持っているので、
x+j , y+i に描画する。
isHit の実装部
function TPiece.isHit(vx,vy:integer):Boolean;
var i,j:integer;
begin
   For i:=0 to 3 do begin
       For j:=0 to 3 do begin
           if shape[i,j] then begin
               if MapBlocks.isBlock(x+vx+j,y+vy+i) then begin
                   Result:=True;
                   Exit;
               end;
           end;
       end;
   end;
   Result:=False;
end;
自分の全ブロックについて以下を実行しています。
           if shape[i,j] then begin
               if MapBlocks.isBlock(x+vx+j,y+vy+i) then begin
                   Result:=True;
                   Exit;
               end;
           end;
さっきと同じですね。で、
shape[i,j]が真、つまり、ブロックがある部分なら、
               if MapBlocks.isBlock(x+vx+j,y+vy+i) then begin
                   Result:=True;
                   Exit;
               end;
自分の持っているフィールドクラスへの参照から、
isBlock関数を呼び出しています。
isBlock関数は、指定した場所がブロックだと真を返すのでした。
座標は、
x(shape[0,0]の座標)+vx(引数として与えられた、移動量)+j(shapeの判定したい場所)
を指定しています。
そして、もし当たったら、
Result、つまり返り値を真にしてそそくさとexit;してしまいます。
もし、一回も当たらなかった場合、
forを抜け、一番最後の、
   Result:=False;
に来るので、返り値には偽が入り、結果、関数は偽を返します。
CopySelfToMapBlocksの実装部
procedure TPiece.CopySelfToMapBlocks;
var i,j:integer;
begin
   For i:=0 to 3 do begin
       For j:=0 to 3 do begin
           if shape[i,j] then begin
               MapBlocks.Blocks[y+i,x+j]:=True;
           end;
       end;
   end;
end;
やっぱり同じループ&判定です。
shape[0,0]がx,yなので、x+j,y+iがフィールドでの対応座標になります。
そこを参照し、真にしています。
ふう、いっぺんに書いちゃいましたが大丈夫でしょうか・・・
まぁ、これは核の処理ではなく、
TPieceを補佐するものなので、
一息ついてから、ここを読み直してみるのもアリだと思います。
(用は、命令が何をするかだけ覚えておいて、使えさえすればおk