第11回:フォント選択ダイアログ



テキストを表示するとき、フォントは特に指定のない限り「MS Pゴシック」となります。

しかし世の中にはそれとは異なるフォントでゲームをプレイしたがる方も多いので、
今回はゲーム内で動的にフォントを変える方法を説明しましょう。
図11-1のように、フォント一覧を表示するダイアログを出して
その中からフォントを一つ選択し、そのフォントでテキストを表示するようにします。


コンボボックスをクリック
図11-1:フォント選択ダイアログ

・インタフェースの設計
フォントの選択は、メインウィンドウとは別のウィンドウを新たに作成して、
そちらで行えるようにします。
メニューの[ファイル|新規作成]から「フォーム」を選択してください。
新しいフォームが一つ作成されます。
ここで一旦[ファイル|すべて保存]を選択し、このフォームを
fontselect.pasという名前で保存します。
オブジェクトインスペクタで、このフォームの設定を次のように変更します。

フォント選択ダイアログのプロパティ
プロパティ名 設定値 備考
Name FontDialog フォームの名前
AutoScroll False スクロールバーを出さない
BorderIcons.biMaximize False 最大化ボタンを非表示に
BorderStyle bsSingle フォームのサイズを固定
ClientWidth 250 ウィンドウ描画領域の幅
ClientHeight 96 ウィンドウ描画領域の高さ
Position poScreenCenter ウィンドウを表示する位置
Caption フォントの選択 タイトルバーの文字

ボタンを二つと、コンボボックスを一つ、FontDialogに配置します(図11-2)。

図11-2:フォント選択ダイアログへのGUI部品配置

配置したボタンとコンボボックスのプロパティを設定します。

OKボタンのプロパティ
プロパティ名 設定値
Name BtnOK
Caption OK
ModalResult mrOK
Cancelボタンのプロパティ
プロパティ名 設定値
Name BtnCancel
Caption Cancel
ModalResult mrCancel
コンボボックスのプロパティ
プロパティ名 設定値
Name CbxFont
Style csDropDownList

ModalResultプロパティを設定すると、そのボタンを押された時に、
プロパティに設定された値を呼び出し側(ここでは基本部分main.pas)に返して
ダイアログが閉じるようになります。
また、コンボボックスのStyleプロパティをcsDropDownListにすると、
コンボボックスに表示されているテキストの編集を行えない
ドロップダウンリストとなります。

デフォルトのフォント名を格納する変数DefaultFontと、
選択中のフォントを格納する変数UseFontを、fontselect.pasでグローバルレベルで宣言します。
この二つは、呼び出し側からも使用できるように、type節の上に記述します。

            
(fontselect.pas内)
unit fontselect;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
            
            
const
   //デフォルトのフォント名
   DefaultFont : string = 'MS ゴシック';
var
   //使用するフォント名
   UseFont : string;
            
            
type
            

イベント処理として、ダイアログが作成されたとき(即ち、ゲームが起動したとき)の
イベントFormCreateと、ダイアログが表示されたとき(基本部分から呼び出されたとき)の
イベントFormShow、OKボタンが押された時のイベントBtnOKClickを用意します。
すべてオブジェクトインスペクタのイベントタブから雛形を生成できます(リスト26)。

FormCreateでは、ゲームで使用できるフォントの一覧を取得してコンボボックスに
格納する処理を行います。
FormShowでは、ゲームで使用中のフォントをコンボボックスに格納されている
フォント一覧から探し出し、表示する処理を行います。
BtnOKClickは、コンボボックスで選択されているフォントをゲームで使用する
フォントに格納する処理を行います。
ModalResultプロパティを設定しているので、ダイアログを閉じる処理を記述しなくても
閉じるようになります。

(リスト26)
            
//ダイアログが作成される時に発生するイベント
procedure TFontDialog.FormCreate(Sender: TObject);
            
            
var
   //フォント一覧を呼び出すクラス
   FL : TFontList; ---------------------------------------------(39)
   //フォント一覧を格納するオブジェクト
   AllFont : TStringList;
            
            
begin
            
            
   //デフォルトフォントを通常使用するフォントに設定する
   UseFont := DefaultFont;
   
   //ゲームで使用可能なフォントを取得する
   FL := TFontList.Create;
   AllFont := TStringList.Create;
   try
      FL.GetFontList(AllFont);

      //取得したフォント名一覧をコンボボックスに格納する
      CbxFont.Items := AllFont;
   finally
      //オブジェクトを解放する
      FL.Free;
      AllFont.Free;
   end;
            
            
end;

//-------------------------------------------------------------

//ダイアログが出現したときに呼ばれるイベント
procedure TFontDialog.FormShow(Sender: TObject);
            
            
var
   //ループカウンタ
   i : Integer;
            
            
begin
            
            
   //コンボボックスの中に格納されている
   //フォントの中から、現在選択しているフォントを見つける
   for i := 0 to CbxFont.Items.Count - 1 do
      if CbxFont.Items[i] = UseFont then
      begin
         //選択しているフォント名をコンボボックスに表示
         CbxFont.ItemIndex := i;

         //forループを抜ける
         Break;
      end;
            
            
end;

//-------------------------------------------------------------

//OKボタンを押した時に発生するイベント
procedure TFontDialog.BtnOKClick(Sender: TObject);
begin
            
            
   //選択されているフォント名をUseFontに代入してダイアログを閉じる
   UseFont := CbxFont.Items[Cbxfont.ItemIndex];
            
            
end;
            

これでインタフェース部分は完成です。
(39)で宣言されているTFontListはゲームで使用するフォント一覧を呼び出すクラスです。
次にこのクラスを実装します。

・フォント一覧の取得

フォント名の一覧はどうやって取得すればよいのでしょう?

プログラム内にフォント名を思いつく限り入れておく…というのはダメですね。
フォントは後からどんどん作られていくものです。
仮に現存する全てのフォント名を書き込んだとしても(相当な量になると思いますが)
数年後にはもっとフォントの種類は増えていることでしょう。
それよりも、代入しておいたフォントは、全てのユーザのマシンに
インストールされているわけではないのです。

フォント一覧を取得するだけなら、ScreenオブジェクトのFontsプロパティで取得できます。
試しに、新規にプロジェクトを一つ作成してボタンとリストボックスを一つずつ配置し、
(名前はデフォルトのButton1,ListBox1で結構です)
Button1のClickイベントに次のように書きます。

procedure TForm1.Button1Click(Sender: TObject);
begin
   ListBox1.Items := Screen.Fonts;
end;

実行してみましょう。
Button1を押すと、マシンにインストールされている
全てのフォント名がリストボックスに書かれます(図11-3)。

図11-3:インストールされている全てのフォントを列挙

はい、何かおかしいところに気づいた方もいらっしゃることでしょう。
図11-3で表示されているようなフォントは、ゲームで使うことができません。

何故か?
フォントが日本語をサポートしていないからです。
ゲームで使うフォントは
これだけの条件が必要となります。
当然ながら、上で表示したプログラムではこんな識別はできません。

かなり面倒な方法ではありますが、
APIでフォントの情報を取得していくことにします。

クラスTEffectを作成した時と同じようにユニットを一つ作成し、
fontlist.pasという名前で保存します。
fontselectで実装するのではなく、新たにユニットを作成して実装するのは、
今後フォント選択をダイアログベース以外のインタフェースで行う場合に、
フォント一覧の取得プログラムを使い回せるようにするためです。

クラスTFontListのメンバ関数GetFontListの宣言と実体を記述します(リスト27)。

(リスト27)
            
(fontlist.pas)
unit fontlist;

interface
            
            
uses
   Windows, Classes;
   
type
   TFontList = class
   public
     procedure GetFontList(p : TStringList);
   end;
            
            
implementation
            
            
//コールバック関数(必要な回数だけWindowsが呼び出す)
function EnumFontFamExProc(var lpelfe : ENUMLOGFONTEX;
                           var lpntme : NEWTEXTMETRIC;
                           FontType : Cardinal;
                           lParam : Integer): Integer; stdcall; ---------(40)
begin
   //True-Typeフォントか
   if (lpntme.tmPitchAndFamily and TMPF_TRUETYPE) > 0 then
      //固定ピッチフォントか
      if (lpelfe.elfLogFont.lfPitchAndFamily and 3) = FIXED_PITCH then
         //横向きのフォントではないか
         if lpelfe.elfLogFont.lfFaceName[0] <> '@' then
            //引数の文字列リストにこのフォント名を追加
            TStrings(lParam).Add(lpelfe.elfLogFont.lfFaceName);

   //関数の戻り値として1を返す
   Result := 1;
end;

procedure TFontList.GetFontList(p : TStringList);
var
   //デバイスコンテキスト
   DC : HDC;
   //フォント情報を指定する構造体
   logfont : TLogFont;
begin

   //取得するフォントの特性を入れる
   logfont.lfCharSet := SHIFTJIS_CHARSET; -------------------------------(41)
   logfont.lfFaceName[0] := #0; -------------|

   //プライマリモニタのデバイスコンテキストを取得する
   DC := GetDC(0);
   try
      //フォントを列挙するAPI関数を呼び出す
      EnumFontFamiliesEx(DC, logfont, @EnumFontFamExProc,
                         Integer(Pointer(p)), 0); -----------------------(42)
   finally
      //デバイスコンテキストを解放する
      ReleaseDC(0, DC);
   end;
end;

            
            
end.
            

手続きGetFontListでキーとなるのが(42)のEnumFontFamiliesExです。
この関数は、二番目の引数logfontで指定された特性のフォントを列挙するWin32APIです。
詳しい使い方は、MicrosoftのMSDNライブラリ
(http://www.microsoft.com/japan/developer/library/jpgdipf/_win32_enumfontfamiliesex.htm)を
参照してください。
二番目の引数logfontは(41)にて特性を代入しています。
lfCharSetには条件であるShift-JISの文字セットを指定し、
lfFaceNameにはヌル文字として「#0」を代入します。
三番目の引数はコールバック関数EnumFontFamExProc APIへのポインタです。
コールバック関数EnumFontFamExProcは、EnumFontFamiliesExがフォントを
一つ列挙する度に、そのフォントの情報を持って呼び出されます。
関数の中身は(40)で記述します。引数が表しているものは、以下の通りです。
関数EnumFontFamExProcでは、この引数を用いて列挙されたフォントが条件に合った
フォントであるかどうかを判別し、条件を満足していれば
引数lParamが指しているTStringListにそのフォント名を加えるという処理を行います。
条件に合っているかどうかの判別は、以下のように行います。
最後に、関数の戻り値を1とすることにより、次のフォントが列挙されるようになります。
フォントの列挙が終了した後は、手続きGetFontListの引数pに
ゲームで使用できるフォント名の一覧が格納されています。

・フォント選択ダイアログの呼び出し
さて、フォント一覧を取得する処理を実装しました。
最後にこれを呼び出す処理を実装しましょう。

fontselect.pasを開いて[ファイル|ユニットを使う]から「fontlist」を選択してください。
uses節が追加され、フォント選択ダイアログからTFontListを使えるようになります。
次に、main.pasを開いて[ファイル|ユニットを使う]から「fontselect」を選択します。
これでフォント選択ダイアログを基本部分から呼び出せるようになりました。

ゲーム中でフォント選択ダイアログを呼び出すには、ポップアップメニューで
「フォントの選択...」を選択したら出現するようにしましょう。
PopupMenuのメニューデザイナを開いて、新規メニュー項目を作成します。
設置場所は、「ロード」とセパレータの間にしました。

オブジェクトインスペクタでプロパティの設定を行います。

「フォントの選択...」メニューのプロパティ
プロパティ名 設定値
Caption フォントの選択...
Name MnuFont

Captionの「フォントの選択」の後に「...」とあるのは一般的な規則です。
選択した後に何らかの追加設定を行う場合に付けます。
この場合は、フォントの選択ですね。

メニューデザイナ上で「フォントの選択...」メニューをダブルクリックして、
クリックイベントMnuFontClickの雛形を生成してください。
処理内容を記述します(リスト28)。

(リスト28)
            
//フォント変更メニューをクリックしたときに発生するイベント
procedure TMainForm.MnuFontClick(Sender: TObject);
begin
            
            
   //フォント選択ダイアログをモーダル表示し、
   //戻り値がmrOKだったら
   if FontDialog.ShowModal = mrOK then
   begin
      //テキストのフォントを変更する
      TextBmp.Canvas.Font.Name := UseFont;
      //現在のテキストを再描画する
      PutText(TextPoint);
   end;
            
            
end;
            

フォント選択ダイアログを表示し、戻り値がmrOK、即ちOKボタンが押されていれば、
テキストの表示フォントをダイアログのコンボボックスで選択したフォントに変更し、
現在表示されているテキストをそのフォントで再描画します。

起動直後などの、一度もフォント選択ダイアログを呼び出していない場合は
表示フォントをデフォルトのフォントにするよう、TextBmpの初期設定に追加します。

            
procedure TMainForm.FormCreate(Sender: TObject);
var
   i : Integer;
begin
(中略)
   TextBmp := TBitmap.Create;
   with TextBmp do
   begin
      Width := TEXT_W;
      Height := TEXT_H;
      Canvas.Font.Size := 15;
      Canvas.Brush.Color := clBlack; //塗りつぶし色は黒
      Canvas.Font.Color := clWhite; //文字の色は白
      Transparent := True;
            
            
      Canvas.Font.Name := fontselect.DefaultFont;
            
            
   end;

   AllText := TStringList.Create;
   AllText.LoadFromFile('..\\..\\data\\scenario.txt');
(以下省略)
            

それでは実行してみましょう。
起動後にマウス右クリックのポップアップメニューで「フォントの選択...」を選択すると、
図11-2で作成したダイアログが出現したでしょうか。
うまくいっていれば、コンボボックスには図11-1のようにマシンにインストールされている
「ゲームで使用できるフォント」の一覧が表示されています。
お好きなフォントを選択してOKボタンを押してみてください。
テキストがそのフォントで表示されているのを確認できたら、成功です。


第12回へ
アドベンチャーゲーム設計論トップへ戻る
開発分室へ戻る
トップページへ戻る