./index.html ../index.html

Pascalを知らない人でもDelphiで
10分…は無理か…30分でゲームの設定ダイアログを作る方法

ゲーム作成者はほとんどがC++を用います。
そして、C++でダイアログを作るのは面倒なものです。
しかし、折角DelphiにはPersonalが存在するので、これを用いて、なるべく少ないコード量で手っ取り早く作ってしまおう、という例です。

ここで想定しているのは、ゲームの設定ダイアログが単独の.exeになっているタイプのものです。

.iniではなくXMLにしてMyBaseで…なら、真に自動生成以外のコードを書かずに作ることもできたのですが、.iniがいいと言ってくれた人がいるので.iniなのです。 従って、若干コードを書かせることになるのは容赦ください。

まず、[ファイル]-[新規作成]-[アプリケーション]でVCLアプリを作成し、[ファイル]-[プロジェクトに名前を付けて保存]で保存します。
ユニットの名前は適当でいいです。 プロジェクトの名前が.exe名になります。 しかし、ここで落とし穴があって、ユニット名とプロジェクト名に同じ名前が使えないのです。 最初に保存ダイアログが出てくるのはユニットの方なので、ここで使いたい名前を入れてしまうとハマります。

プロジェクトができたら、RADでフォームを作成します。
ここで用いるフォームは、デフォルトで作成されているフォームのみなので、どこから.exeがスタートするのかとか、どこでウィンドウを作るのかとか、細かいことは考えなくていいです。
不安があれば、何も作業していない状態でも、[F9]を押せば空のウィンドウを出す.exeができることを確認してください。

まずはForm1のプロパティ。
なお、RADで作成したオブジェクトには、ビジュアルかどうかを問わず、オブジェクトツリーを用いることで手っ取り早く操作できます。

次に、StandardのActionListを設置します。
アクションは、VCLではModel-View-ControlerのControlerに相当するもの(ちょっと違う)で、フォームとコードの掛け橋となり、動作を定義するものです。*1

アクションリスト

標準アクションの新規追加でTFileExitを追加します。 これはウィンドウを閉じる動作です。TWindowCloseはMDI版なので注意。 なお、今回はダイアログに見せかけて実は普通のアプリケーションなので、閉じるためにファイルメニューの終了に相当する動作を用いるわけですが、本当にダイアログボックスを作る時は(もっと手っ取り早い)別の方法*2があります。

また、新規にアクションを作り、"Save"と名づけます。(Nameプロパティに"Save"を入力)

次に、PageControl、と、Buttonふたつを配置します。
タブ順の設定が面倒なので、TABキーで移動して欲しい順番に配置します。なので、PageControl、OKとなるボタン、Cancelとなるボタン、の順です。

OKとなるボタンは次のようにプロパティを設定します。

Cancelとなるボタンは次のようにプロパティを設定します。

いよいよダイアログの各項目に入ります。
PageControlを右クリックして「ページ新規作成」で好きなだけページを作ります。
タブに表示されるテキストはページ部分のCaptionです。

本当は様々なコントロールを駆使したいのですが、コードをなるべく単純にするため、使用するものはLabelとComboBoxに限定します。

Labelは、やはりCaptionがテキストです。
"&a"のようなアクセラレーターを付けた場合は、FocusControlで組になるComboBoxを指定します。

ComboBoxはこんな感じです。

ここでは(Nameを)DisplayModeとFogModeとしました。

フォームのActiveControlプロパティに、最初にフォーカスを置くところ、ここではDisplayModeを指定して、RAD作業は終了です。

フォーム作成例
こんなフォームを作りました。

以下はコードでの作業となります。

OKButtonをダブルクリックしてイベントハンドラを生成します。
"OK"が押された時の処理です。
「セーブする」「終了する」とふたつのアクションを行うため、作ったアクションを続けて呼びます。

procedure TForm1.OKButtonClick(Sender: TObject);
begin
  Save.Execute;
  FileExit1.Execute
end;

オブジェクトツリーの"Save"アクションをダブルクリック。
セーブ処理です。

procedure TForm1.SaveExecute(Sender: TObject);
var
  Ini: TMemIniFile;
begin
  Ini := TMemIniFile.Create(ExtractFilePath(Application.ExeName) + 'YeiWORD.ini');
  try
    Ini.WriteString('Display', 'DisplayMode', DisplayMode.Text);
    Ini.WriteString('Display', 'FogMode', FogMode.Text);
    Ini.UpdateFile;
  finally
    Ini.Free;
  end;
end;

必要なだけWriteStringを連ねてください。
最初がセクション名、次が項目名、最後に値ですが、これは手抜きの為、選択されたテキストをそのまま用います。

try文は"tryf"Ctrl+Jで勝手に入力してくれます。

最後に、TMemIniFileはIniFilesが提供しているので、uses節に書き加えます。

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ActnList, StdActns, IniFiles;

実行例
実行例。

で、.ini出力例。

[Display]
DisplayMode=640x480
FogMode=頂点

プロジェクトオプション
仕上げとして、プロジェクトオプションでタイトルやアイコンを変えればいいでしょう。

さて、この状態では、読み込みを実装していないため、起動するたびに全項目がリセットされてしまいます。
それはもちろん、この手のチュートリアルにありがちですが、あなたへの課題という方向で。
うーむ、いい言葉ですね。課題の一言で手抜きが正当化できます。

…あんまりか。
そもそもこの手の課題と称して面倒なことを押しつける型のテキストにろくなものが無いという経験則?を思い出しましたので、作ります。

Save同様にLoadアクションを作成し、フォームのOnCreateから呼びます。

procedure TForm1.FormCreate(Sender: TObject);
begin
  Load.Execute
end;

procedure TForm1.LoadExecute(Sender: TObject);
var
  Ini: TMemIniFile;
begin
  Ini := TMemIniFile.Create(ExtractFilePath(Application.ExeName) + 'YeiWORD.ini');
  try
    DisplayMode.ItemIndex := DisplayMode.Items.IndexOf(
      Ini.ReadString('Display', 'DisplayMode', DisplayMode.Text));
    FogMode.ItemIndex := FogMode.Items.IndexOf(
      Ini.ReadString('Display', 'FogMode', FogMode.Text));
  finally
    Ini.Free;
  end;
end;

おしまい。


*1 たとえ、それがキー→動作ではなく動作→キーな設定をするのでキー割り当ての概念が不自然だったり、複数のキーを同じ動作に割り当てるのが面倒だったり割り当てたところで表示に反映されるのは最初のひとつだけだったり…(以下略)。

*2 参考までに書いておけば、ボタンのModalResultを設定するだけです。 完全にダイアログとして動作するフォームを作り、それを呼び出しても良かった(つーか確実にその方がいい)のですが、その場合プロジェクトソースを書き変えなければならないため、今回はなるべくPascalコード量を減らしたかったために避けました。