11-7 「インベーダー生成」ボタン

「操作盤」フォームに「インベーダー生成」ボタンを作り、このボタンを押すと新しいインベーダーが次々に生成される、というプログラムに改変します。左のようにフォームにボタンを付け加え、次のような名前にしてください。

コンポーネント Name プロパティ
「インベーダー生成」ボタン ButtonGenerate

今までは、インベーダーは4機 (ボスインベーダーなどを除く) と固定されていましたが、これから作り変えるプログラムでは、インベーダーの機数は特に定まっていません (実行中に何機でもインベーダーを作り出すことが出来るからです)。そのため、インベーダーを初期化する部分

  Invader1.Construct; // インベーダー1を出現させる
  Invader2.Construct; // インベーダー2を出現させる
  Invader3.Construct; // インベーダー3を出現させる
  Invader4.Construct; // インベーダー4を出現させる

や、インベーダーを移動させる部分

  Invader1.Move; // インベーダー1を動かす
  Invader2.Move; // インベーダー2を動かす
  Invader3.Move; // インベーダー3を動かす
  Invader4.Move; // インベーダー4を動かす

では、初期化文や移動文をいくつ書いていいか分からず、困ってしまいます。

このように、ある命令文を何度繰り返すか、が実行中でないと定まらない場合は、for 文 (や while 文) が適していましたね。しかし、Invader1, Invader2, Invader3, Invader4 という名前でインベーダーが用意 (宣言) されていても、このままでは for 文で使える形 (変数 i などを使って呼び出せる形) ではありません。そこで、各インベーダーの宣言

var
  Invader1: TInvader;
  Invader2: TInvader;
  Invader3: TInvader;
  Invader4: TInvader;

を次のように書き換えます。

var
  Invaders: Array of TInvader;

この1行で、Invaders[0], Invaders[1], Invaders[2], Invaders[3], …と、(0番から名付けられる) いくつものインベーダーを宣言したことになります。…ですが実際には、この命令だけで無限の機数のインベーダーのためのメモリを確保することは不可能なので、SetLength という手続きによって「何機分用意するか」を宣言する必要があります。

【練習問題】

「表示」→「プロジェクト マネージャ」で、プロジェクトファイル「invader_game.exe」を右クリックして「ソース表示」を選び、プログラムを次のように変更しましょう。

begin
  Application.Initialize;
  Application.CreateForm(TFormControl, FormControl);
  Application.CreateForm(TFormDisplay, FormDisplay);
  SetLength(Invaders, 4); // 「インベーダーは4機」と宣言
  Invaders[0] := TInvader.Create;
  Invaders[1] := TInvader.Create;
  Invaders[2] := TInvader.Create;
  Invaders[3] := TInvader.Create;
  Bullet1 := TBullet.Create;
  InvaderBoss := TInvaderBoss.Create;
  Application.Run;
end.

【練習問題】

今の練習問題で変更した部分を、更に for 文を使った形へと改良しましょう。

var
  i: Integer;
begin
  Application.Initialize;
  Application.CreateForm(TFormControl, FormControl);
  Application.CreateForm(TFormDisplay, FormDisplay);
  SetLength(Invaders, 4); // 「インベーダーは4機」と宣言
  for i :=0 to 3 do
    begin
      Invaders[i] := TInvader.Create;
    end;
  Bullet1 := TBullet.Create;
  InvaderBoss := TInvaderBoss.Create;
  Application.Run;
end.

【基礎課題 11-9】

上の練習問題と同様に

と見なして for 文で書きかえるべき場所が、このプログラムには2ヶ所残っています。この2ヶ所を探し出して修正し、プログラムを実行してください。

これで下準備は終わり、いよいよインベーダーを増やすことが出来ます。例えば、5機目のインベーダーを生成するためには、生成したい場所 (例えば「インベーダー生成」ボタンの OnClickイベントハンドラ) で次のような命令を書き込むことになるでしょう。

  SetLength(Invaders, 5); // 「インベーダーは5機」と宣言
  Invaders[4] := TInvader.Create;
  Invaders[4].Construct; // インベーダー5を出現させる

【練習問題】

上の例では、5機目のインベーダーを作り出すことは出来ますが、6機目以降のインベーダーを作り出すことは出来ません。6機目以降のインベーダーも作り出せるように変更しましょう。なお、「今までにいくつのインベーダーが生成されたか」は、Length(Invaders) で分かります。

「インベーダー生成」ボタンのイベントハンドラは、次のようにします。

// 「インベーダー生成」ボタン
procedure TFormControl.ButtonGenerateClick(Sender: TObject);
begin
  SetLength(Invaders, Length(Invaders) + 1); // 「インベーダーは1機増える」と宣言
  Invaders[Length(Invaders) - 1] := TInvader.Create;
  Invaders[Length(Invaders) - 1].Construct; // 新しいインベーダーを出現させる
end;

最後に、タイマーが時を刻む毎にインベーダーを移動させる部分の for 文の繰り返し回数を修正しておきます。

// タイマーが時間を刻むごとに
procedure TFormControl.TimerMainTimer(Sender: TObject);
var
  i: Integer;
begin
  for i := 0 to Length(Invaders) - 1 do
    begin
      Invaders[i].Move; // インベーダーを動かす
    end;
  InvaderBoss.RandomMove; // ボスインベーダーを動かす
  Bullet1.Move; // 弾を動かす
end;

これで、プログラム実行中でも「インベーダー生成」ボタンを押すことによってインベーダーを増やすことが出来るようになりました。

ここまでのプログラムをダウンロード(D)