■ 新・ゲーム開発講座




■ へっぽこプログラミング入門♪


■第5夜:ちょっと豪華?な文字表示

前回は、既定フォント、既定サイズ、既定色で単に文字を出力するだけでした。ところで、文字表示をもう少しゴージャスにしたい場合はどうしましょう。TextOut()は現在設定されているフォント情報を用いて単にテキストを画面に書き出しているだけです。すると文字表示に変化を付けたい場合には、フォント情報(種類、大きさ、表示角度など)を事前に設定してから TextOut() を呼べば良さそうですね。
TextOutの出力に飾りを付けるのに役に立ちそうな関数としては、以下のようなものがあります。

CreateFont()
SetTextColor()
SetBkMode()
フォントの種類、大きさなどを指定します
出力テキストに色を付けます
文字の背景部分をどう扱うか指定します





■フォントの使い方

フォントを指定して文字を出力する処理の流れの例を簡単に書くと、以下のようになります。



HFONT hfont,oldfont; //フォントへのハンドルを宣言


//フォントオブジェクト作成
hfont=CreateFont(
20,
0,
0,
0,
400,
0,
0,
0,
SHIFTJIS_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE,
"MS 明朝"
//フォント高さ
//フォント幅(0なので自動設定扱い♪)
//回転表示はしない
//回転表示はしない
//フォント太さ(0-1000で100単位:標準=400)
//TRUEにするとイタリック設定になったり(^^)
//下線の有無(0:なし)
//打消線の有無(0:なし)
//キャラクタセット(シフトJIS固定で問題ない)
//出力精度デフォルト
//クリップ精度デフォルト
//文字品質デフォルト
//ピッチもデフォルト
//フォント種類

);

oldfont=(HFONT)SelectObject(hdc,hfont);

TextOut(hdc,100,200,”ABCDE”,5);

SelectObject(hdc,oldfont);
DeleteObject(hfont);
//フォント指定

//文字表示

//フォント指定戻し
//フォントオブジェクト削除


「オブジェクトって何だ?」と思われる方もおられるかと思いますが、あまり気にしないで結構です。面倒な処理を水面下でやってもらうためのデータ一式の塊と思ってください(乱暴 ^^;)。そのデータ一式を識別するための背番号が HFONT hfont , oldfont で宣言しているフォントへのハンドルです。

処理はまず、フォントオブジェクトを生成するところから始まります。hfont=CreateFont(・・・)でフォントオブジェクトを作成するとともに、そのハンドルを取得します。パラメータが多くて面倒ですが(笑)、多くの場合デフォルト設定で間に合ってしまいます。当面の目標であるノベル製作では、せいぜいフォントの大きさが変えられれば十分でしょう。

次に、oldfont=(HFONT)SelectObject(hdc,hfont) で、生成したフォントオブジェクトを選択すると同時に旧フォントオブジェクトへのハンドルを取得しておきます。SelectObject() の後は、文字表示はすべて新しいフォントに切り替わります。
新しい内容の設定を行ったときに古い設定が戻り値として戻ってくるのは不思議な感じがするかも知れませんが、処理の最後で設定を元に戻すために (使い終わったらすぐに旧設定に戻せ、というMSの推奨だそうです) このような仕様になったみたいです…(^^)

そして、TextOut() などでフォントを使用し終ったら、再度 SelectObject() で旧設定を復活させます。そして用済みになったフォントオブジェクトを DeleteObject(hfont) で削除します。

一見、面倒な作業ですが、Windows上でプログラミングを行う上では避けて通れない儀式のようなものですので、こればかりは慣れるしかありません。面倒だと思ったら、本項の最後に示す StrPut3D() のようにラッピング(”包む”の意)関数を定義してしまえば良いでしょう。

※フォントオブジェクトは、明示的に削除しない限りメモリ上に存在しつづけます。削除を忘れたままプログラムを走らせていると・・・メモリ食いつぶしでハングアップするもとですのでご注意を・・・・(^^)

■文字の背景処理

文字表示に限りませんが、デバイスコンテキストの背景処理の方法を決めるには SetBkMode() というAPIを使用します。

int SetBkMode(HDC hdc, int mode);

hdcは操作対象となるデバイスコンテキストのハンドル、mode は設定モードです。設定モードは実は2つしかありません。OPAQUE = 背景塗りつぶしTRANSPARENT = 背景を残す の2種類です。ゲームでは文字を表示したら背景のグラフィックが塗りつぶされてしまった・・・というのではみっともないので、大抵の場合必然的にTRANSPARENTを選ぶことになります。

なお、戻り値として以前の設定モードが返って来るのですが、別に元に戻さなくても実害はないので放っておいても大丈夫でしょう(をい)。

【補足:シンボルについて】
ところで、OPAQUEとかTRANSPARENTって文字は何だ?…と思われる方がいるかもしれません。これはソースの頭の方で #include(=取りこむ) してある windows.h のそのまた内部で #include されている wingdi.h で定義されているシンボルです。現在のバージョンのVC++ではそれぞれ1と2の値が設定されています。単に数字が書いてあるよりも意味のある(…と言っても英語圏の人にとってですが ^^)単語を使用したほうが分かりやすいプログラムになるだろう、という発想です。WindowsやVCに限らず、最近のプログラミングではこの種のシンボルが大量に使われる傾向にあります。初心者にとっては「良くわからない呪文が飛び交っている(^^;)」ように見えるかも知れませんが、結局は1とか2とかの定数を文字列で置き換えているだけなので無用の心配はしなくても良いでしょう。

■文字に色をつける

文字に色をつけるAPIは、その名も SetTextColor() …そのまんまです(笑)。

COLORREF SetTextColor(HDC hdc, COLORREF clColor);

引数は2つ。どの画面に対して操作を行うかをデバイスコンテキストで指定(HDC hdc)して、色情報 clColor を渡してやるだけです。ここで COLORREF という見なれない型が出てきましたが、単にRGB:3チャンネルの8bitデータを合体したもの(サイズとしてはDWORD=32bit)です。
RGB(r,g,b)というマクロが使えるので普段はこれで充分な気がします(^^) 戻り値は「以前の色」になりますが、これも放っておいて実害はありません(以後、同じ色設定が継続するだけです)。

【例】
SetTextColor(hdc,RGB(255,255,255)); //ここでは白を指定しています

【補足:色】
いまどきRGBって何?という人も少ないとは思いますが、一応解説を(笑)。RGBとは Red、Gren、Blue の光の3原色です。絵の具の色などは原色混合なので赤、青、黄の合成になりますが、ディスプレイのように光る物体では加色混合になるので赤、緑、青で全ての色を表現します。一般的な画像処理ではRGBは各色8bit(0〜255)で表されます。
面倒な理屈は置いといて、ここでは 赤=RGB(255,0,0)、緑=RGB(0,255,0)、青=RGB(0,0,255)、白=RGB(255,255,255)、黒=RGB(0,0,0) 程度が分かっていれば十分でしょう。あとは適当に数値をいじって納得してください〜♪(笑)

■影付き文字を表示してみよう

ちょっとした応用として、影つき文字列を表示する関数の例を以下に示します。関数名の「3D」というのは語弊があるかも知れませんが(笑)、まあ細かいことは気にしないでいきましょう(^^)。よく見ると CreateFont()などは引数がみ〜んなデフォルト設定のような感じですが・・・まあそれはそれとゆーことで(笑)

ここでは、フォントはMS明朝(標準で入っているのでどのPCでも表示できる)に固定して、サイズと色を変えながら表示しています。影付きといっても、色を変えたフォントをちょっとだけずらして2度書きしているだけなんですけどね(笑)



int StrPut3D(HDC hdc,int x,int y,int fontSize,UINT color1,UINT color2,char *str)
// HDC:デバイスコンテキストへのハンドル
// x,y:表示座標
// fontSize:フォントのサイズ
// color1:文字の色
// color2:影の色
// str:表示したい文字列へのポインタ

{

HFONT hfont,oldfont;

hfont=CreateFont(
fontSize,
0,
0,
0,
600,
0,
0,
0,
SHIFTJIS_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE,
"MS 明朝"
//フォント高さ
//フォント幅(0なので自動設定扱い♪)
//回転表示はしない
//回転表示はしない
//フォントちょい太め(0-1000で100単位:標準=400)
//TRUEにするとイタリック設定になったり(^^)
//下線は無し
//打消線は無し
//キャラクタセットはWin95ならシフトJIS固定
//出力精度デフォルト
//クリップ精度デフォルト
//文字品質デフォルト
//ピッチもデフォルト
//ゴシックよりは見栄えがするでしょう(^^)

);

if(hfont==NULL){
MessageBox(NULL,"フォント失敗","えらー",MB_OK);
return 0;

}

SetBkMode(hdc,TRANSPARENT); //背景の「ふちどり」はしない

//影の部分を表示
SetTextColor(hdc,color2); //色指定
oldfont=(HFONT)SelectObject(hdc,hfont); //フォント指定
TextOut(hdc,x+1,y+1,str,strlen(str)); //表示
SelectObject(hdc,oldfont); //フォント指定戻し

//表の部分を表示
SetTextColor(hdc,color1); //色指定
oldfont=(HFONT)SelectObject(hdc,hfont); //フォント指定
TextOut(hdc,x,y,str,strlen(str)); //表示
SelectObject(hdc,oldfont); //フォント指定戻し

DeleteObject(hfont);

return 0;


}


さて StrPut3D()の引数を見ると、HDC hdc というのがありますね。これは、ウィンドウやBMPなどの画像オブジェクトを扱うときに必ず使用するデバイスコンテキスト(DC)というデータ構造へのハンドルです。第1夜のスケルトンをよく見ると、WinMain()中でウィンドウを表示した直後に

win_hdc=GetDC(hwnd);

でDCを取得しています。ウィンドウにアクセスする際には、ライブラリ関数やAPIにこのDCのハンドルを渡して「このウィンドウ(画像)の基本情報はここに入っているから参照してね」 という使い方をします。StrPut3D() では、表示文字列の背景処理を設定する SetBkMode() がこのDCへのハンドルを要求しているので引数に加えてある次第です。

これを使ったサンプルソースを
ここに示します。コンパイル→実行すると以下のような画面になります。いろいろいじって遊んでみてください♪(^^)