11-1 コンポーネントを変数・関数・手続きに

10-2 の【基礎課題 10-1】で、FormInvader, FormInvaderBoss, FormBulletVisibleプロパティを False にしても、プログラムは正しく動きましたね。これはつまり、フォーム上の全てのコンポーネントは画面に表示される必要がないことを意味します。さらに考えると、コンポーネントではなく、同等の機能をもつ変数・手続きだけでも用が足りる、ということになります。

【練習問題】

第3章で EditTemp の代わりに変数 temp を用いたように、FormInvader のコンポーネントを順次変数に置き換えてみましょう。invader ユニットの冒頭部を見てください。次のようになっているはずです (コンポーネントの順序がこれと違うかもしれませんが、問題ありません)。この節は、ここを見ながら作業すると理解しやすくなります。

unit invader;

interface

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

type
  TFormInvader = class(TForm)
    LabelX: TLabel;
    LabelY: TLabel;
    LabelSpeed: TLabel;
    SpinEditX: TSpinEdit;
    SpinEditY: TSpinEdit;
    ButtonDraw: TButton;
    ButtonErase: TButton;
    ButtonGoHorizontal: TButton;
    ButtonGoDown: TButton;
    ButtonMove: TButton;
    SpinEditSpeed: TSpinEdit;
    CheckBoxAlive: TCheckBox;
    ButtonConst: TButton;
    ButtonHitCheck: TButton;
    ButtonDest: TButton;
    procedure ButtonDrawClick(Sender: TObject);
    procedure ButtonEraseClick(Sender: TObject);
    procedure ButtonGoHorizontalClick(Sender: TObject);
    procedure ButtonGoDownClick(Sender: TObject);
    procedure ButtonMoveClick(Sender: TObject);
    procedure ButtonConstClick(Sender: TObject);
    procedure ButtonDestClick(Sender: TObject);
    procedure ButtonHitCheckClick(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

ではまず、SpinEditX を変数にします。SpinEditX で使われているプロパティは Valueだけです。これは整数しか扱わないプロパティなので、代わりに整数型変数 X を用意することにします。(なお、型を忘れた場合は「F1」キーを押して調べることができます。) まずは、インベーダーフォーム上の SpinEditX を選び、「編集」→「削除」で消してください。ユニット冒頭部からも、SpinEditXが消えているはずです。次に、public と書かれた行の次に下のように書き加えてください。

  private
    { Private 宣言 }
  public
    { Public 宣言 }
    X: Integer;
  end;

最後に、ユニット中に出てくる全ての「SpinEditX.Value」を「X」に書き換えてください。

コラム すべて置換

ユニットを最初から最後まで全て眺めて「SpinEditX.Value」を「X」に書き換える作業は大変面倒です。こういう場合は、Delphi のメニューバーにある「検索」→「置換」を用いると便利です。

このように「検索文字列」と「置換文字列」を設定して「すべて置換」を押すと、プログラム中に出てくる全ての SpinEditX.Value について1つ1つ「置換するかどうか」をたずねてきて、楽に置換することができます。(ただし、置換して良いかどうかは常に確認する様に心がけましょう。例えばSpinEditY.Value という検索文字列を指定した場合には FormBullet.SpinEditY.Value など他のフォームのコンポーネントもひっかかるので、うっかり置換してしまう可能性があります。)

書き換えが終わったら実行してみましょう。今までどおり実行できましたか?

【基礎課題 11-1】

SpinEditX と同様に、SpinEditY を変数 Y に、SpinEditSpeed を変数 Speed に書き換えましょう。なお、この書き換えで、「未定義の識別子」というエラーが他のユニットから出ることがあります。このエラーが出たら、そのユニットでも「SpinEditYY」「SpinEditSpeedSpeed」と名前を変更してください。

【練習問題】

フォーム上のラベルは単なる表示ですから (フォームが画面に表示されないと意味がないので)、全て削除してしまっても構いませんね。全てのラベルについて、「編集」→「削除」を用いて消してしまってください。

ボタンは消してしまっても大丈夫でしょうか? 第5章で学んだように、ボタンを削除してもそのイベントプロシージャは残ります。他のユニットから呼び出しているのはボタンそのものではなく手続き (イベントハンドラ) なので、ボタンを消してしまっても大丈夫です。ラベルと同様に全てのボタンを消してしまってください。

消し終わったら実行して動作を確認しましょう。

【練習問題】

ボタンがなくなったのですから、「procedure ButtonDrawClick(Sender: TObject)」というように手続きの名前をボタンに合わせておく必要はありません。また、引数がいらない単なる手続きにするのですから、Sender: TObject という引数も不要になります。

ユニットの最初のほうのtype 宣言に出てくる「ButtonDrawClick(Sender: TObject)」を

    CheckBoxAlive: TCheckBox;
    procedure Draw; virtual;
    procedure ButtonEraseClick(Sender: TObject);

と変え、ユニットの後半に出てくる「ButtonDrawClick(Sender: TObject)」を

    // 「描く」ボタン
    procedure TFormInvader.Draw;
    begin

と変えましょう。

type 部における手続きの宣言で、なぜ virtual という命令を付けなければならないのか気になった人もいると思います。今までは出てきませんでしたからね。その意味については、11-4 で説明します。ここでは、宣言部では virtual をつけておいた方がよい、と考えておいてください。

また、「ButtonDrawClick(Sender)」を呼び出している部分も全て「Draw」に書き換えましょう (ユニットの中に3ヶ所あります)。

書き換えが終わったら、実行して動作を確認しましょう。

【基礎課題 11-2】

同様に、他の手続きの名前も書き換えましょう。(前半の「type」宣言では「virtual;」を忘れないように。)

元の手続き名 新しい手続き名
ButtonEraseClick Erase
ButtonGoHorizontalClick GoHorizontal
ButtonGoDownClick GoDown
ButtonMoveClick Move
ButtonConstClick Construct
ButtonDestClick Destruct
ButtonHitCheckClick HitCheck

なお、これらの手続きは control ユニットからも呼び出されているので、そちらの書き換えも忘れないようにしましょう。(書き換え忘れたまま実行しようとすると「未定義の識別子」というエラーが出ます。)

もし「未定義の識別子 : 'Sender'」というエラーがどうしても残る場合は、とりあえず「Sender」の部分を「FormInvader1」にしておいてください。

【練習問題】

さて、最後に CheckBoxAlive だけが残ってしまいました。これは何型の変数にしたら良いのでしょうか。どうやら整数型でも文字列型でもなさそうです。

チェックボックスで必要なプロパティは Checked だけなので、オブジェクトインスペクタで Checked をクリックし、その状態で「F1」を押してヘルプを出してください。

すると、Checked プロパティが何型であるか、がわかります。

Checked プロパティは,チェックボックスがチェックされた状態かどうかを指定します。

property Checked: Boolean;

説明

Checked プロパティを使うと,チェックボックスがチェックされているかどうかを判定できます。Checked は,チェックボックスがチェックされているときは True,チェックされていないときや淡色表示のときは False です。

どうやら、Boolean という型のようです。CheckBoxAliveBoolean 型の変数 Alive に代え、実行してみましょう。

type
  TFormInvader = class(TForm)
    procedure Draw; virtual;
    procedure Erase; virtual;
    procedure GoHorizontal; virtual;
    procedure GoDown; virtual;
    procedure Move; virtual;
    procedure Construct; virtual;
    procedure Destruct; virtual;
    procedure HitCheck; virtual;
  private
    { Private 宣言 }
  public
    { Public 宣言 }
    X: integer;
    Y: integer;
    Speed: integer;
    Alive: Boolean;
  end;

【基礎課題 11-3】

ボスインベーダーフォームに残る「乱数移動」ボタンを削除し、ButtonRandomMoveClick(Sender: TObject)RandomMove に変更してください。(invader_boss ユニットでは2ヶ所、そして control ユニットで1ヶ所変更することになります。)

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