プログラムをよりビジュアルにしたいと思った時、不可欠なのがグラフィックでしょう。単に見栄えをよくするということだけでなく、例えばゲームなどの場合、グラフィックの描画や操作ができないとゲーム自体が作れません。ここでは、Delphiのグラフィックの基本について説明しましょう。
グラフィックと一口にいっても、いくつかのやり方があります。1つは、あらかじめグラフィックのデータを用意しておいて、それをコンポーネントなどに設定して表示するというやり方。もう1つは、描画用の命令などをプログラムの中から実行して描いていくやり方です。
まず、もっとも簡単な「コンポーネントに表示する」方法からです。Delphiには、グラフィックファイルをプロパティに設定して表示することのできるコンポーネントがあります。それは「Image」というものです。グラフィックを利用する場合、このImageコンポーネントを使うのが基本と考えましょう。このコンポーネントは、コンポーネントパレットの「Addtional」タブの中に用意されています。
Imageは、透明な枠だけのコンポーネントです。この状態では何も表示されません。描画の命令を実行したり、表示するグラフィックを設定したりすると、初めて画面に表示されるようになります。
グラフィックの表示は簡単です。Imageには「Picture」というプロパティがあり、ここで表示するグラフィックファイルを設定すると、自動的にそれが表示されるようになっているのです。オブジェクトインスペクタから「Picture」を選び、その右端にある「・・・」ボタンをクリックすると、画面に「画像の設定」というダイアログが現れます。
ここで、「読み込み」ボタンをクリックし、表示したいグラフィックファイルを選択すると、それが画像の設定ダイアログに表示されます。それでよければ「OK」ボタンを押してダイアログを閉じると、Imageコンポーネントに選択したグラフィックが表示されます。
このグラフィックは、コンパイルをするとEXE自体に取り込まれるため、作ったアプリケーションでは元のグラフィックファイルは必要なくなります。
さて、Imageに表示したグラフィック(イメージと呼びます)は、通常はそのまま等倍で表示されますが、以下のようなプロパティを設定することで、表示の仕方を変えることもできます。
AutoSize――Trueにすると、Imageの大きさをイメージの大きさに自動調整する。
Stretch――Trueにすると、Imageの大きさにあわせてイメージを変形し表示する。
Transparent――Trueにすると、イメージの周辺部の色を透明にして表示する。
これらを覚えておくと、イメージをいろいろとアレンジして表示できてとても便利です。特にTransparentは、いくつかのイメージを重ねて表示したりするのにとても重宝します。
次に、プログラムの中から命令などを使ってグラフィックを描く方法に進みましょう。これには、まずDelphiのコンポーネントにおけるグラフィックの仕組みについて理解しておく必要があります。
Delphiでは、グラフィック関係の機能は「Canvas」というプロパティにまとめられています。これはTCanvasというオブジェクトが設定されたプロパティです。オブジェクト、覚えてますか? さまざまな値や機能をもったかたまりみたいなものでしたね?
このTCanvasのオブジェクトには、グラフィック描画のためのさまざまな値や、描画命令の類いがまとめられています。オブジェクトの中にある値は「プロパティ」、命令に相当するものは「メソッド」と一般に呼ばれます。つまり、プロパティを使って描画の基本的な設定を行ない、描画用のメソッドを呼び出すことで、そのコンポーネントに図形などが描かれる、というような仕組みになっているわけです。
とりあえず、細かなプロパティはおいといて、描画のためのメソッドから説明しましょう。Canvasには、描画用のメソッドがいくつかありますが、もっとも基本となるのは以下のものでしょう。
・四角形を描く
《Canvas》.Rectangle( 左 , 上 , 右 , 下 );・円形を描く
《Canvas》.Ellipse( 左 , 上 , 右 , 下 );・描画開始地点を移動する
《Canvas》. MoveTo( 横 , 縦 );・直線を描く
《Canvas》. LineTo( 横 , 縦 );
ちょっとわかりにくいのは「直線の描画」でしょう。Canvasでは、常に「描画の開始地点」というものが記憶されています。図形を描くと、描き終わった地点に開始地点が設定されるようになっているんですね。直線を描くLineToは、この開始地点から、()で指定した地点まで直線を描くメソッドなのです。
LineToで線を描くと、描き終わった地点に開始地点は移動します。ですから続けてLineToを実行すれば、描いた直線の最後の地点から続けて次の線が描かれるようになります。
では、簡単なサンプルをあげておきましょう。
procedure TForm1.Button1Click(Sender: TObject); begin Image1.Canvas.Rectangle(0,0,100,100); Image1.Canvas.Ellipse(50,50,150,150); Image1.Canvas.MoveTo(0,0); Image1.Canvas.LineTo(150,150); end;
これは、四角形・円形・直線を描画する簡単なサンプルです。ボタンのOnClickに設定し、プロジェクトを実行してボタンを押してみましょう。このように図形が描かれますよ。まぁ、このように単純に図形を描くだけなら、これらのメソッドは割とすぐに使えるようになるはずです。
ところで、この図形描画のサンプルでちょっと不思議なことに気づいた人はいませんか? よく見ると、四角形の上に円形がのっかって描かれていますね? つまり、描いた図形は輪郭だけのものでなく、中を白で塗りつぶした図形だったわけです。だから、先に描いた四角形が隠れてしまったんですね。
では、中を塗りつぶさないようにして図形を描くにはどうすればいいんでしょう。普通、思い浮かぶのは「きっと枠だけを描くメソッドがあるんだ」というような考え方でしょう。しかしDelphiではちょっと違う考え方をします。それは「塗りつぶしの方式を変えて、描画メソッドを呼び出せばいい」と考えるのです。
Canvasには描画のためのさまざまなプロパティが用意されていますが、中でももっとも重要なのは「Pen」と「Brush」というプロパティです。これは、それぞれ「線」と「塗りつぶし」に関するオブジェクトが設定されたプロパティなのです。
Canvasでは、このPenとBrushで、線と塗りつぶしに関する全ての設定を行ないます。そして描画メソッドは、単に「図形を描くだけ」のものになってるのです。「どんな色で」とか「線の太さはどのぐらいで」なんてことは描画メソッドは全く知りません。ただ「こう描く」ということしか知らないのです。描く図形の具体的な設定は、PenとBrushによって行なわれるのですね。
では、このPenとBrushのオブジェクトにどんなプロパティが用意されているのか、いくつか主だったものをあげておきましょう。
・Penオブジェクトのプロパティ
Color――線の色を示す。TColorで設定する。
Width――線の太さを示す。整数で設定する。
Style――線のスタイル(実線、点線など)を示す。TPenStyleというタイプで以下のいずれかに設定。
(psSolid, psDash, psDot, psDashDot, psDashDotDot, psClear, psInsideFrame)
Mode――線の描画モードを示す。TPenModeというタイプで以下のいずれかに設定。
(pmBlack, pmWhite, pmNop, pmNot, pmCopy, pmNotCopy, pmMergePenNot, pmMaskPenNot, pmMergeNotPen, pmMaskNotPen, pmMerge, pmNotMerge, pmMask, pmNotMask, pmXor, pmNotXor)・Brushオブジェクトのプロパティ
Color――塗りつぶしの色を示す。TColorで設定する。
Style――塗りつぶしのパターンを示す。TBrushStyleというタイプで、以下のいずれかに設定。
(bsSolid, bsClear, bsHorizontal, bsVertical, bsFDiagonal, bsBDiagonal, bsCross, bsDiagCross)
Bitmap――ブラシ模様となるビットマップを示す。TBitmapというビットマップ用オブジェクトで設定する。
なんだかよくわからないものも出てきましたね。StyleやModeは、あらかじめ用意されている値の中のいずれかを設定するようになっています。が、そもそも「それを設定するとどうなるのか?」がよくわからないかも知れません。またBitmapも「ビットマップのオブジェクトって?」と考え込んでしまいそうです。
とりあえず、今の段階ではColorとWidthだけ理解しておけばいいでしょう。それとBrushのStyleから「bsSolid」と「bsClear」だけ覚えておきましょう。bsSolidは全て中身を塗りつぶすスタイルで、bsClearは全く塗りつぶさないというスタイルです。つまり、BrushのStyleプロパティをbsClearにすれば、輪郭だけで中を塗りつぶさない図形が描けるってわけです。
では、簡単な例を上げましょう。BrushとPenのColorを変化しながら図形を描くようなプロシージャを考えてみます。
procedure TForm1.Button1Click(Sender: TObject); var i: Integer; begin Image1.Canvas.Brush.Style := bsSolid; Image1.Canvas.Pen.Width := 5; for i := 1 to 10 do begin Image1.Canvas.Pen.Color := RGB(25 * i,0,0); Image1.Canvas.Brush.Color := RGB(25 * i,255,255); Image1.Canvas.Ellipse(i * 10,i * 10,i * 10 + 50,i * 10 + 50); end; end;
こんな感じでしょうか。このように「PenとBrushのプロパティを変更する」「描画メソッドを実行する」というのを繰り返していけば、思い通りに図形を描けるようになります。まぁ、ちょっと面倒臭い感じはするでしょうが、CanvasとPen、Brushの構造と役割がしっかり理解できれば、そう大変ではなくなるはずです。
上のサンプルがだいたい理解できたら、それぞれでPenとBrush、そして描画メソッドを使っていろいろと図形を描いてみましょう。何度かコードを書いてどんな図形が描かれるか確かめてみれば、使い方はすぐに飲み込めますよ。
イメージの表示、図形の描画とくれば、残るは両者をあわせた「イメージの描画」というのでしょう。Imageにイメージを設定して表示するだけでなく、イメージを必要に応じて自由に描画できれば、表現力は格段にアップしますから。
イメージの描画は、「あらかじめ元のイメージを設定したImageコンポーネントを用意しておき、そのイメージを表示用のImageに描画する」というように考えます。つまり「元データ用のImage」と「表示用のImage」を用意しておくわけですね。元データのImageは、VisibleプロパティをFalseにしておけば、画面に表示されません。
イメージの描画には、いくつかのメソッドが用意されています。ここで簡単に紹介しておきましょう。
《Canvas》.Draw( 横 , 縦 , 《TGraphic》 );
指定の位置にTGraphicのイメージを描画する。《Canvas》.StretchDraw( 《TRect》 , 《TGraphic》 );
イメージを指定の領域だけ切り抜き、描画先の指定領域に変型して描画する。
ちょっとわかりにくいかも知れませんね。まず、両者とも「描画するのはTGraphicというオブジェクトである」ということを理解しておきましょう。これはグラフィックを扱うための専用オブジェクトです。Imageでは、Pictureプロパティにイメージを読み込んでますが、このPictureをそのまま描画に使うわけにはいかないのです。Pictureオブジェクトの中に、実際に描画などに使うための「Graphic」というプロパティが用意されています。これを《TGraphic》に指定します。
1つ目のDrawは、ごく単純に 《TGraphic》のものをCanvasの指定した位置に描画します。2つ目のものは、《TGraphic》のある部分を切り抜いて、Canvasの指定した部分にはめ込むようにして描画します。といっても、実際にそのグラフィックの一部を切り抜いちゃうわけじゃないですよ? 指定の部分だけをコピーして取り出す、というように考えるといいでしょう。
問題は、領域の指定に「TRect」ってタイプの値を指定しないといけないってことです。これは、領域を示すのに多用されるタイプで、こんな感じで作ることができます。
《TRectの変数》 := Rect( 横 , 縦 , 横幅 , 縦幅 );
似たようなものに、位置の指定に用いる「TPoint」ってタイプの値もあります。これもあわせて紹介しておきましょう。こんな具合に作ります。
《TPointの変数》 := Point( 横 , 縦 );
グラフィック関係では、このTRectとTPointで位置や領域を指定することが多いので、「こうやって作ればいい」ってことを頭に入れておくと将来役立つでしょう。
さて、とりあえずはDrawの方を使って「イメージを丸ごと他のImageに描画する」というやり方をしっかり覚えておきましょう。簡単なサンプルをあげておきます。
procedure TForm1.Button1Click(Sender: TObject); var x,y: Integer; begin try x := StrToInt(Edit1.Text); y := StrToInt(Edit2.Text); Image1.Canvas.Draw(x,y,Image2.Picture.Graphic); except Beep; end; end;
これは、Edit1とEdit2という2つのコンポーネントを用意しておき、そこに描く縦横の位置を書き込んでからボタンをクリックすると、Image1の指定した位置にImage2のイメージを描画する、というプロシージャです。
やっているのは割と簡単なことですね。まず2つのEditからそれぞれ数字を変数に取り出します。そして、Image1.Canvas.Drawを使って、取り出した位置にイメージを描画します。描画するイメージの指定は、Image2.Picture.GraphicというようにImageのPictureプロパティ内のGraphicオブジェクトを指定してやります。これで、決まった位置に図形を描画することができるというわけです。
なお、サンプルの図ではイメージの周りが透過し、きれいに重ね合わせて描画されていますが、これはImage2のTransparentをTrueにしてあるためです。ImageのTransparentプロパティは、Drawで描画する際にもきちんと機能してくれるんですね。
これで、Drawによる描画の仕方がだいたいわかったら、StretchDrawの描画にも挑戦してみるといいでしょう。これも使えるようになれば、イメージ描画の基本はほぼマスターできますよ。