プログラム4 紙芝居

通常ゲームでは前回のサンプルのように、直接フォームに画像を描画することは殆どしま
せん。
何故ならば、フォームに直接描画すると、描画している様子が見えるとまでは行かなくて
も、ちらついて見えたりする可能性が高いからです。
一度だけ表示する、とか言うのであればともかく、アニメーションなどをするとそれが顕
著に現れます。
ですので、バッファに表示する画像を一旦作成し、それをフォームにコピーするといった
手法をとる必要があります。
こういうバッファを、バックバッファなどという呼び方をしたりします。
このバックバッファに画像を一旦描画するわけですが、前のサンプルのように、毎回画像
ファイルからデータを読み込むのでは速度的な面からも良くありません。
じゃぁ、どうするのかといえば、簡単な話でメモリにおいておけば良いわけです。
メモリにおいておく、などという書き方をすると難しそうなイメージがあるかもしれませ
んが、単純にクラスのメンバにBitmapオブジェクトを追加するだけです。
基本的には背景と、それに重ね合わせる絵(人物等)という構成になるため、Bitmapオブ
ジェクトは最低2つ必要になります。
背景は一つでかまいませんが、重ね合わせる絵は複数存在するのが普通です。
ですから、コレクションを使うのが理想ですが、今回は一つで良しとしましょう。

さて、AVGには更に文字を表示する必要があります。
画像の上に単に文字を表示すると、文字と背景が混ざって、見づらくなることが有ります。
文字は基本的に白ですから、背景も白だと、全く見えません。
それを防止するために、文字表示用のウィンドウのようなものが必要になってきます。
この文字表示用のウィンドウは、一枚の絵になっていることもあれば、半透明の四角だっ
たりします。
ここではこんなところに拘っても仕方ないでしょうから、まずは半透明の四角をつかいま
しょう。

と、いうわけで、やることが見えてきました。
羅列してみますか。
1)Bitmapオブジェクトをクラスのメンバにする
2)バックバッファを作成し、フォームにコピーする
3)文字表示用ウィンドウを作成する
4)文字を表示する

一つづつ、いきます。
……と、そのまえに、ウィンドウの大きさを変更しておきましょう。
デフォルトでは300x300だと思いますが、これだと小さいのでVGAサイズにしておきます。
フォームのSizeプロパティを640,512に変更してください。
本当は640x480なんですけど、上にあるバー(?)の分を足してやる必要があるのです。

さて、気を取り直して。
1番目は簡単。
単に、クラスのメンバに追加するだけですので、クラス内でBitmapオブジェクトを配置
するだけです。

   public partial class Form1 : Form
   {
       private Bitmap backBmp;
       private Bitmap frontBmp;
       public Form1()
       {
           InitializeComponent();
       }
   }

こんなかんじ。

で、フォームをWクリックして、Loadイベントで画像を読み込むようにしましょう。

       private void Form1_Load(object sender, EventArgs e)
       {
           backBmp = new Bitmap("0001.bmp");
           frontBmp = new Bitmap("0002.bmp");
       }

使ったものは開放する、ということで、フォームのClosedイベントを作成し、使った
BitmapオブジェクトをDispseメソッドにて開放します。

       private void Form1_FormClosed(object sender, FormClosedEventArgs e)
       {
           backBmp.Dispose();
           frontBmp.Dispose();
       }

これでBitmapオブジェクトをクラスメンバして、使う用意ができました。
これを実行しても、何にも表示されません。
フォームに反映してないので当たり前なんですが。

では、バックバッファを利用して、フォームに反映する仕組みを追加しましょう。
バックバッファは、BufferedGraphicsContextとBufferedGraphicsを使います。
BufferedGraphicsContextは、グラフィックバッファの割り当てや管理などを行うクラス
になります。
BufferedGraphicsがバッファクラスになります。

  public partial class Form1 : Form
   {
       private BufferedGraphicsContext currentContext;
       private BufferedGraphics drawBuffer;
      
       private Bitmap backBmp;
       private Bitmap frontBmp;
      
       public Form1()
       
       {
           InitializeComponent();
       }
       
       private void Form1_Load(object sender, EventArgs e)
       {
           currentContext = new BufferedGraphicsContext();
           drawBuffer = currentContext.Allocate(this.CreateGraphics(), this.DisplayRectangle );
       
           backBmp = new Bitmap("0001.bmp");
           frontBmp = new Bitmap("0002.bmp");
       }
       
       private void Form1_FormClosed(object sender, FormClosedEventArgs e)
       {
           backBmp.Dispose();
           frontBmp.Dispose();
           drawBuffer.Dispose();
           currentContext.Dispose();
       }
   }

まず、BufferGraphicsContextのインスタンスを作成します。
インスタンスの作成には幾つかの方法があるのですが、ここではマネージャクラスを介
さず専用のインスタンスを作成しています。
一応オーバーヘッドが少ないという理由によります。
次に、作成したBufferGraphicsContextのAllocateメソッドを使い、バッファの作成と、
対象のGraphicsクラスを関連付けます。
第一パラメータに、対象のGraphicsクラスを指定します。
ここでは当然、フォームのGraphicsクラスになります。
フォームのGraphicsクラスを得るにはCreageGraphicsメソッドを呼びます。
第二パラメータにバッファのサイズを指定します。
Rectangle構造体は四角形の位置とサイズを表します。ここでは、表示されるフォームの
サイズにあわせています。

専用のBufferGraphicsContextを使っているので、破棄も手動で行います。
Closedイベントに、破棄するコードを記述します。

これでバックバッファの使用が可能になりました。
バックバッファのGraphicsプロパティを介し、描画後、Renderメソッドを実行してバッ
ファの内容を対象のGraphicsにコピーします。

       private void Form1_Paint(object sender, PaintEventArgs e)
       {
           Renderer();
       }
       
       private void Renderer()
       {
           drawBuffer.Graphics.DrawImage(backBmp, 0, 0);
           frontBmp.MakeTransparent(System.Drawing.Color.White);
           drawBuffer.Graphics.DrawImage(frontBmp, 0, 0);
           
           drawBuffer.Render();
       }

単純に前回のサンプルのように、DrawImageメソッドを使って描画しているだけです。
全て描画し終わったら、といっても2つしかやってないんですが、Renderメソッドを実行
してフォームに反映させます。
尚、Paintイベント以外でも描画を行う為、描画処理を別メソッドにて記述します。
無論、Paintイベントでも描画メソッドを実行します。

さて、次は文字表示用のウィンドウですね。

       private void Renderer()
       {
           SolidBrush windowBrush = new SolidBrush( Color.FromArgb( 128 , 0 , 0 , 255 ) );
           Rectangle windowRect = new Rectangle( 32 , 384 , 576 , 128 );
           
           drawBuffer.Graphics.DrawImage(backBmp, 0, 0);
           frontBmp.MakeTransparent(System.Drawing.Color.White);
           drawBuffer.Graphics.DrawImage(frontBmp, 0, 0);
           
           drawBuffer.Graphics.FillRectangle(windowBrush, windowRect );
           
           drawBuffer.Render();
           
           windowBrush.Dispose();
       }

前回文字を表示したときのように、SolidBrushで塗る色を決めます。
今回は、Color構造体の、FromArgbメソッドを使い、色を作っています。
FromArgbは、その名の通りA,R,G,Bから色を作成します。
即ち、アルファ値、赤、緑、青、です。
アルファ値というのは、透過度です。
この値が0であれば完全に透過、255で不透明になります。
ここでは半分の透過をする青色のウィンドウを表示するイメージになります。

その下のRectangleは、四角形の大きさと位置を示します。
x座標,y座標,横幅,高さの順に指定します。

四角を描画するのが、FillRectangleメソッドです。
これは、Grahpicsクラスのメソッドです。
パラメータには、使用するBrushと、四角の位置を表すRectangleを渡します。

で、あとは、前回やったのと同じように文字を表示すれば良いわけです。

       private void Renderer()
       {
           SolidBrush windowBrush = new SolidBrush( Color.FromArgb( 128 , 0 , 0 , 255 ) );
           SolidBrush textBrush = new SolidBrush(Color.White);
           Rectangle windowRect = new Rectangle( 32 , 336 , 576 , 128 );
           RectangleF textRectF = new RectangleF( 48 , 350 , 560 , 112 );
           Font messageFont = new Font("MS UI Gothic", 24, FontStyle.Bold, GraphicsUnit.Pixel);
           
           drawBuffer.Graphics.DrawImage(backBmp, 0, 0);
           frontBmp.MakeTransparent(System.Drawing.Color.White);
           drawBuffer.Graphics.DrawImage(frontBmp, 0, 0);
           
           drawBuffer.Graphics.FillRectangle(windowBrush, windowRect );
           
           drawBuffer.Graphics.DrawString( "文字列" , messageFont, textBrush, textRectF );
           
           drawBuffer.Render();
           
           windowBrush.Dispose();
       }

前回と全く同じ、では芸が無い、というわけではないのですが、表示方法をちょっと変
えてます。一つは、フォントを作成、というか、書式の定義をしています。
書式定義にはFontクラスを使用します。
コンストラクタの第1パラメータにはフォント名、第2パラメータには、サイズ、第3
パラメータにはフォントのスタイル、第4パラメータには長さ単位を指定します。
スタイルはBoldとかItalicとかいうあれで、FontStyle列挙体にて定義されています。
長さの単位は、インチとか、ピクセルなどの単位で、ここではピクセルを指定しています。

文字の表示には、前回と同様DrawStringメソッドを使いますが、前回とは異なり、
第4のパラメータにRectangleF構造体を渡しています。
Rectangleと異なるのは、Fという名がついていることからわかるとおり、浮動小数で位
置や大きさが指定できます。
ま、ここでは整数しか指定しませんが、細かい位置指定ができるという事です。
尤も、単位がピクセルだとあまり意味ありませんが。
で、このRectangleFを渡すと、指定した四角形の範囲内に文字を表示するようになりま
す。
しかも、一行に表示する文字が、枠の範囲を超えると、改行までしてくれます。
さらに、禁則処理もしてくれるようです。
禁則処理ってのは、たとえば、行頭が句読点だった場合は、句読点の一文字前で改行す
る、といった処理です。
自前で改行や禁則処理のルーチンを入れなくて良いのは楽ですね。
ちょっと寂しい気もしますが。

文字表示用のウィンドウと、文字列が表示されたでしょうか。

ちなみに、このままだと、フォントがギザギザに表示されてしまい、格好が悪いです。
GDI+では、標準でアンチエイリアスがかけられるようになっています。
DrawStringの前に、以下のコードを追加してみましょう。

           drawBuffer.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

フォントが滑らかになって表示されたと思います。
Grpahics.TextRenderingHintプロパティは、表示する文字のテキストレンダリングの品
質を決めるプロパティです。
更に品質を上げることもできるのですが、その分パフォーマンスも低下しますので、ア
ンチエイリアスだけをかけるのが良いかと思います。
一度決めてしまえば保持されるので、drawBufferを作成した後に設定するのが正解です
かね。
最終更新:2007年12月14日 13:01
ツールボックス

下から選んでください:

新しいページを作成する
ヘルプ / FAQ もご覧ください。