−トップへ−

ブロックくずしゲームを作る
その7 最終回

 このシリーズの最後です。今回は,今まで作成したプログラムソースを修正していきます。
 まずは,ラケットでボールを跳ね返すとき,ラケットが動いている場合にボールの進む角度が変わるようにします。
 また,ボールがスタートする場所も乱数を使って変化するようにします。
 
 
 まず,ボールがスタートする場所が変化するようにします。プログラムを書く場所は,FormKeyDownイベントハンドラです。

 procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
 begin
  if key=VK_right then keyd:=4;
  if key=VK_left then keyd:=-4;
  if key=VK_space then begin
   //ボールのスタート場所に変化を持たせる
   randomize; //乱数の初期化
   ballx:=random(200)+100; //X座標で100〜299の間でボールがスタートするようにする
   bally:=300;
   bx:=(random(2)*2-1)*3; //ボールの進む横方向を -3または3にする
   by:=-6;
   balls:=6;
   with image1.Canvas do begin
    brush.Color:=clblack;
    fillrect(rect(0,200,image1.Width,450));
   end;
   timer1.Enabled:=true;
  end;
 end;

 random(200)は,0〜199の数字を返してきます。random(2)は,0か1の数字を返してきます。ですから(random(2)*2-1)*3とすると,-3か3のどちらかの数字を返してきます。
 
 
 次にラケットでボールを跳ね返すとき,ラケットが動いている場合にボールの進む角度が変わるようにします。プログラムを書く場所は,Timer1Timerの中です。


 前回までのプログラムでは以下の通りでした。

  //ボールがラケットに当たったか調べる
  if bally>=raky-balls then
  begin
   if (ballx>=rakx) and (ballx<=rakx+rakw) then by:=-by;
  end;

 上記の部分を下のように書きかえます。

  //ボールがラケットに当たったか調べる
  if (bally>=raky-balls) and (bally<raky+5) then begin //ボールがY座標でラケットの位置に来たとき
   if (ballx>=rakx) and (ballx<=rakx+rakw) then begin //ボールがX座標でラケットの位置にあるとき
    by:=-by; //ボールのY座標の進行方向を反対にする
    if keyd>0 then bx:=bx+2; //[→]キーが押されていたら,進行方向を右に傾ける
    if keyd<0 then bx:=bx-2; //[←]キーが押されていたら,進行方向を左に傾ける
   end;
 end;

 次に,ブロックを全部消したら,新しいブロックを表示するようにします。
 
 これを行うには,消したブロックの数を数えるために,変数「score」を宣言します。

 private
  { Private 宣言 }
  ballx,bally,bx,by,balls,score:integer;//Private部に,scoreを整数宣言します
  rakx,raky,rakw,keyd:integer;
  block:array[0..10,0..2] of boolean;
 public
  { Public 宣言 }
 end;

 そして,Timer1Timerイベントハンドラで,ブロックが消えるごとにscoreを1ずつ増やしていき,30になったら新しいブロックを表示するようにします。

  //ブロックに当たったか調べる
  if (bally>=60) and (bally<=70) then begin //下段のブロックで
   i:=ballx div (image1.Width div 10);
   if block[i,2]=true then begin //ブロックに当たったら
    block[i,2]:=false;
    by:=-by;
    with image1.Canvas do begin
     brush.Color:=clblack;
     fillrect(rect(i*(image1.Width div 10),60,(i+1)*(image1.Width div 10),70));
    end;
    inc(score); //scoreを1増加する
   end;
  end;
  if (bally>=40) and (bally<=50) then begin //中段のブロックで
   i:=ballx div (image1.Width div 10);
   if block[i,1]=true then begin //ブロックに当たったら
    block[i,1]:=false;
    by:=-by;
    with image1.Canvas do begin
     brush.Color:=clblack;
     fillrect(rect(i*(image1.Width div 10),40,(i+1)*(image1.Width div 10),50));
    end;
    inc(score); //scoreを1増加する
   end;
  end;
  if (bally>=20) and (bally<=30) then begin /上段のブロックで
   i:=ballx div (image1.Width div 10);
   if block[i,0]=true then begin //ブロックに当たったら
    block[i,0]:=false;
    by:=-by;
    with image1.Canvas do begin
     brush.Color:=clblack;
     fillrect(rect(i*(image1.Width div 10),20,(i+1)*(image1.Width div 10),30));
    end;
    inc(score); //scoreを1増加する
   end;
  end;
   //ブロックがなくなったとき
  if score mod 31 =0 then begin //scoreを31で割った余りが0のとき(scoreの初期値は1にしたため,31で割っています)
   timer1.Enabled:=false; //timer1を無効にする
   inc(score); //scoreを1増加する(これをしないと,次回も「score mod 31=0」になってしまう)
   makeblock; //makeblockイベントハンドラを実行して,ブロックを表示させる
   exit; //timer1Timerイベントハンドラから抜け出す
  end;

 最後に,「Game Over」のときの処理をします。

 ここでは失敗しても,ボールを3個まで使用できるようにして,3個を使い切った後は,スペースキーを押してもボールが出ないようにします。
 まず,private部でballk(整数型にして,ボールの個数を表します)とgameover(論理型にして,trueならゲームオーバーにします。)を宣言します。
 
 これらを,すべてまとめて,プログラムソースを表示します。

 unit test1;
 
 interface
 
 uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;
 
 type
  TForm1 = class(TForm)
  Image1: TImage;
  Timer1: TTimer;
  procedure makeBlock;
  procedure FormCreate(Sender: TObject);
  procedure Timer1Timer(Sender: TObject);
  procedure FormKeyDown(Sender: TObject; var Key: Word;
   Shift: TShiftState);
  procedure FormKeyUp(Sender: TObject; var Key: Word;
   Shift: TShiftState);
 private
  { Private 宣言 }
  ballx,bally,bx,by,balls,score,ballk:integer; //scoreは消したブロックの数,ballkは使用できるボールの数
  rakx,raky,rakw,keyd:integer;
  block:array[0..10,0..2] of boolean;
  gameover:boolean; //gameoverはtrueでゲームオーバー,falseでゲーム継続
 public
  { Public 宣言 }
 end;
 
 var
  Form1: TForm1;
 
 implementation
 
 {$R *.dfm}
 
 procedure TForm1.FormCreate(Sender: TObject);
 begin
  form1.ClientWidth:=400;
  form1.ClientHeight:=500;
  image1.Left:=0;
  image1.Top:=0;
  image1.Width:=form1.ClientWidth;
  image1.Height:=form1.ClientHeight;
  ballk:=3; //ボールの個数を3個に設定
  gameover:=false; //gameoverをfalseに設定
  score:=1; //scoreを1に設定
  rakx:=200;
  raky:=450;
  rakw:=40;
  with image1.Canvas do begin
   brush.Color:=clblack;
   fillrect(image1.Canvas.ClipRect);
    //ラケットを描く
   brush.Color:=clwhite;
   fillrect(rect(rakx,raky,rakx+rakw,raky+10));
  end;
  makeblock;
 end;
 
 procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
 begin
  if key=VK_right then keyd:=4;
  if key=VK_left then keyd:=-4;
  if key=VK_space then begin
   if gameover=true then exit; //もしgameoverがtrueなら以下のプログラムを実行しない
    //ボールの出方に変化を持たせる
   randomize; //乱数の初期化
   ballx:=random(200)+100; //ballxの値を100〜299にする
   bally:=300;
   bx:=(random(2)*2-1)*3; //bxの値を-3または3にする
   by:=-6;
   balls:=6;
   with image1.Canvas do begin
    brush.Color:=clblack;
    fillrect(rect(0,200,image1.Width,450));
   end;
   timer1.Enabled:=true;
  end;
 end;
 
 procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
 begin
  keyd:=0;
 end;

 //===============================================
 //ブロックを表示する=============================
 procedure TForm1.makeBlock;
 var
  i,j:integer;
 begin
  for i:=0 to 2 do begin
   for j:=0 to 9 do begin
    block[j,i]:=true;
   end;
  end;
  with image1.Canvas do begin
   for i:=0 to 2 do begin
    brush.Color:=clyellow;
    for j:=0 to 9 do begin
     fillrect(rect((image1.Width div 10)*j,20+20*i,(image1.Width div 10)*(j+1)-1,30+20*i));
    end;
   end;
  end;
 end;
 
 procedure TForm1.Timer1Timer(Sender: TObject);
 var
  i:integer;
 begin
  with image1.Canvas do begin
   //ボールがあった部分を黒く塗る
   brush.Color:=clblack;
   fillrect(rect(ballx,bally,ballx+balls,bally+balls));
  end;
  //ボールの新しい座標を求める
  if (ballx<=0) or (ballx>=image1.Width-balls) then bx:=-bx;
  ballx:=ballx+bx;
  if bally<=0 then by:=-by;
  bally:=bally+by;
  //ボールがラケットに当たったか調べる
  if (bally>=raky-balls) and (bally<raky+5) then begin
   if (ballx>=rakx) and (ballx<=rakx+rakw) then begin
    by:=-by;
    if keyd>0 then bx:=bx+2;
    if keyd<0 then bx:=bx-2;
   end;
  end;
  //ゲームオーバー
  if bally>=500 then begin
   timer1.Enabled:=false;
   dec(ballk);//ボールの個数を1つ減らす
   if ballk>0 then begin//ボールの個数が0より大きかったら
    with image1.Canvas do begin
     font.Color:=clred;
     font.Size:=30;
     textout((image1.Width-textwidth('ボール残り'+inttostr(ballk)+'個')) div 2,200,
                      'ボール残り'+inttostr(ballk)+'個');
                      //ボールの残り個数を表示
    end;
   end else begin
    //ボールの残り個数が0になったとき
    gameover:=true;//vameoverをtrueにする
    with image1.Canvas do begin
     font.Color:=clred;
     font.Size:=30;
     textout((image1.Width-textwidth('Game Over')) div 2,200,'Game Over');
     //「Game Over」を表示
    end;
   end;
  end;
  //ブロックに当たったか調べる
  if (bally>=60) and (bally<=70) then begin
   i:=ballx div (image1.Width div 10);
   if block[i,2]=true then begin
    block[i,2]:=false;
    by:=-by;
    with image1.Canvas do begin
     brush.Color:=clblack;
     fillrect(rect(i*(image1.Width div 10),60,(i+1)*(image1.Width div 10),70));
    end;
    inc(score); //scoreに1加える
   end;
  end;
  if (bally>=40) and (bally<=50) then begin
   i:=ballx div (image1.Width div 10);
   if block[i,1]=true then begin
    block[i,1]:=false;
    by:=-by;
    with image1.Canvas do begin
     brush.Color:=clblack;
     fillrect(rect(i*(image1.Width div 10),40,(i+1)*(image1.Width div 10),50));
    end;
    inc(score); //scoreに1加える
   end;
  end;
  if (bally>=20) and (bally<=30) then begin
   i:=ballx div (image1.Width div 10);
   if block[i,0]=true then begin
    block[i,0]:=false;
    by:=-by;
    with image1.Canvas do begin
     brush.Color:=clblack;
     fillrect(rect(i*(image1.Width div 10),20,(i+1)*(image1.Width div 10),30));
    end;
    inc(score);//scoreに1加える
   end;
  end;
  //ブロックがなくなったとき
  if score mod 31 =0 then begin
   timer1.Enabled:=false;
   inc(score);
   makeblock;//新しくブロックを表示する
   exit;
  end;
 
  //bx,by進んだ位置にボールを描く
  with image1.Canvas do begin
   brush.Color:=clwhite;
   fillrect(rect(ballx,bally,ballx+balls,bally+balls));
  end;
  //ラケットを動かす
  if keyd<>0 then begin
   with image1.Canvas do begin
    brush.Color:=clblack;
    fillrect(rect(rakx,raky,rakx+rakw,raky+10));
    rakx:=rakx+keyd;
    brush.Color:=clwhite;
    fillrect(rect(rakx,raky,rakx+rakw,raky+10));
   end;
  end;
 end;

 まだまだ改良する点はありますが,以上で終了します。
 今までのプログラムを,ダウンロードできるようにしましたので,興味のある人は見てください。
 なお,ダウンロードファイルは,「Delphi XE2」で作成したものです。ですので「Delphi XE2」以降のものがないと開くことができません。
 
 また,このプログラムは適当に改良して再配布しても構いませんので,ご自由にお使いください。
 

 「ブロックくずし」プログラムファイル

−トップへ−