11-6 クラスとユニット

プログラム実行中はインベーダーフォームやボスインベーダーフォームは表示されなくなりましたが、プログラム設計中はまだフォーム (方眼のついたフォーム) が表示されます。

ボスインベーダーフォームの上で右クリックし、「エディタで表示」を選んでください。

すると、次のようなコードが表示されます。(あなたのインベーダープログラムでは、一部の値がこれと違うかも知れませんが、気にする必要はありません。)

object FormInvaderBoss: TFormInvaderBoss
  Left = 710
  Top = 449
  Width = 377
  Height = 340
  Caption = 'ボスインベーダー'
  Color = clBtnFace
  Font.Charset = SHIFTJIS_CHARSET
  Font.Color = clWindowText
  Font.Height = -12
  Font.Name = 'MS Pゴシック'
  Font.Style = []
  OldCreateOrder = False
  Visible = True
  PixelsPerInch = 96
  TextHeight = 12
end

今までオブジェクトインスペクタで設定したプロパティの内容は、このような形で記憶されています。(そして、このコードを直接書き換えると、例えば「Caption = 'ボスインベーダー'」を「Caption = '大侵略者'」と書き換えるとフォームもその通りに変更されます。)

そして、このコードの上の方を見ると、ファイル名が「invader_boss.dfm」となっていることがわかります。つまり、「invader_boss.dfm」というファイルがインベーダーフォームのデザインをつかさどっているのです。"dfm" は "Delphi ForM" ファイルの略です。各プロジェクトのフォルダの中に「.dfm」で終わるファイルが存在することは、Delphi を使い始めた頃から知ってますね。(確認したらコード上で右クリックし、「フォームとして表示」を選んで元の状態に戻してください。)

ですが、実行中にはインベーダーフォームは使われていないため、このファイルも不要です。

【練習問題】

これからこの invader_boss.dfm ファイルを削除します。まずは、この invader_boss.dfm ファイルを参照している部分の記述を消していき、最後に invader_boss.dfm ファイルを削除します。

(1) invader_boss ユニットの中ごろにある {$R *.DFM} という行 (下の下線部) を削除してください。

implementation

uses display, bullet;

{$R *.DFM}

// 「乱数移動」ボタン
procedure TFormInvaderBoss.RandomMove;
var

※ {$R *.DFM} は一見コメントのように見えますが、実は dfm ファイルを見つけて実行ファイルにリンクさせる命令 (コンパイラ指令といいいます) だったのです。これを削除すると、dfm ファイルがあってもフォームが作成されません。

(2) プロジェクトファイル (開き方は 11-5 の【練習問題】を参照すること) の次の部分を削除してください。

uses
  Forms,
  control in 'control.pas' {FormControl},
  display in 'display.pas' {FormDisplay},
  invader in 'invader.pas' {FormInvader},
  bullet in 'bullet.pas' {FormBullet},
  invader_boss in 'invader_boss.pas' {FormInvaderBoss};

(3) これで、invader_boss.dfm ファイルを削除することができます。「ファイル」→「すべて閉じる」で作成中のプログラムを一旦閉じ、invader_boss.dfm ファイルを削除し、「ファイル」→「プロジェクトを開く」で再度開いてみてください。特に、その状態から「表示」→「プロジェクトマネージャ」を選ぶと、他のユニットとの違いがよくわかります。

この invader_boss.dfm ファイルは、「ファイル」→「フォームの新規作成」を選んだ段階で自動的に作られたものです。もし最初からフォームのないユニットを作るなら、「ファイル」→「フォームの新規作成」ではなく「ファイル」→「新規作成」→「ユニット」を選びます。

こうして作られたユニットは、非常にあっさりしています。ここに、先ほどのプログラム

unit invader_boss;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Spin, Invader;

type
  TFormInvaderBoss = class(TFormInvader)
  private
    { Private 宣言 }
  protected
    procedure Draw; override;
  public
    { Public 宣言 }
    procedure RandomMove; virtual;
  end;

var
  FormInvaderBoss: TFormInvaderBoss;

implementation

uses display, bullet;

// 「乱数移動」ボタン
procedure TFormInvaderBoss.RandomMove;
var
  NewSpeed: Integer;
begin
  NewSpeed := GetSpeed + Random(7) - 3;
  SetSpeed(NewSpeed);
  Move;
end;

procedure TFormInvaderBoss.Draw;
begin
  FormDisplay.ImageField.Canvas.Pen.Color := clRed; // 赤でインベーダを描く
  FormDisplay.ImageField.Canvas.MoveTo(GetX, GetY - 3);
  FormDisplay.ImageField.Canvas.LineTo(GetX - 5, GetY + 3);
  FormDisplay.ImageField.Canvas.LineTo(GetX + 5, GetY + 3);
  FormDisplay.ImageField.Canvas.LineTo(GetX, GetY - 3);
end;

end.

を書き込むとよいのです。

なお、最初の uses の部分は

uses
  Graphics, Invader;

と簡単にしても構いません。

コラム  なぜ uses の部分は簡単にできるのか

uses の部分は、「このユニットは〜という他のユニットやシステムの機能を使うことがあります」という宣言です。「ファイル」→「フォームの新規作成」を選んだ段階で Delphi が「後で色々使うかもしれない」と考えて勝手に色々宣言しているのですが、そのままでは余計なものが含まれています。

このプログラムでは、下の2つさえ宣言してあれば動きます。

必要な宣言 理由
Graphics FormDisplay のグラフィックスを操作しているため
Invader Invader クラスから継承しているため

【基礎課題 11-7】

invader ユニットも同様に単純なユニットにしてください。

【練習問題】

すでにインベーダーフォームが存在しないのに FormInvader1などの名前はふさわしくありません。FormInvader1FormInvader4 をそれぞれ Invader1Invader4 に、また TFormInvaderTInvader に変えてください。

ボスインベーダーに関しても同様に変えてください。

【基礎課題 11-8】

bullet ユニットも同様に単純なユニットにしてください。なお、上の練習問題と同様に、FormBulletBullet1 に、TFormBulletTBullet にしてください。

コラム フォームとユニットは別物

今までは、フォームとユニットは同じものを指していましたが、実は別物です。最終的に出来上がる実行ファイルに対応するのがプロジェクトであり、そのプロジェクトに直接乗っかっているのがユニットです。1つのユニットには1つのフォームをつけることができます (もちろんフォームがないユニットも作れます)。これを図にすると次のようになります。

TInvaderTBullet のように、型となるもののことをクラスと呼びます。Invader1Bullet1 のように、クラスから作られる各々の実体をオブジェクトと呼びます。

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