DOBON.NET DOBON.NETプログラミング掲示板過去ログ

CreateGraphicsからPaintへの書き換え

環境/言語:[OS : Windows XP / 言語 : Visual Basic 2005 Express / .NET Framework : 2.0]
分類:[.NET]

【解決したい問題】

現在、簡単なシューティングゲームを作っておりますが、
描画する物が多くなるにつれ描画のちらつきが耐えられないレベルになってきました。
そこでダブルバッファリングを試してみようとしたのですが効果が無く、
調べてみたところCreateGraphicsで描画したものはダブルバッファリングの対象にならないと聞き、
全てPaintで行うよう書き換えようと思ったのですが、どうするかよく判りません。
簡略化すると以下の様なコードなのですが、どのように書き換えればよいのでしょうか?
(現状、この3つの処理が1フレーム(Timerで、Interval30として制御しています)に最大で1000回程度行われています。
また、Tickごとに1回Refresh命令を入れています)

*弾の処理…弾は全て●、★等のテキスト
Dim g As Graphics = Picturebox1.CreateGraphics()
Dim fnt As New Font("MS UI GOTHIC", font_size)
Dim color As Brush = font_color
Dim text As String = "●"
g.DrawString(text, fnt, color, X, Y)
fnt.Dispose()
g.Dispose()

*敵・自機の処理…リソースに埋め込んだ画像
Dim g As Graphics = Picturebox1.CreateGraphics()
g.DrawImage(Enemy1, X, Y, Enemy1_width, Enemy1_height)
g.Dispose()

*レーザーの処理…塗りつぶした長方形
Dim g As Graphics = Picturebox1.CreateGraphics()
Dim points As Point() = {New Point(X1, Y1), New Point(X2, Y2), New Point(X3, Y3), New Point(X4, Y4)}
'colorには既にBrush型が入っています
g.FillPolygon(color, points, Drawing2D.FillMode.Alternate)
g.Dispose()

【解決するために何をしたか】

また、

Private Sub Picturebox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Picturebox1.Paint
move_laser(e)
'レーザーの動き。
move_bullet(e)
'弾の動き。
move_enemy(e)
'敵の動き。
move_player_bullet(e)
'自機弾の動き。
hit_and_move(e)
'当たり判定・移動・自機弾射出。
End Sub

このように描画処理を行うサブルーチンを全てPaintイベントの中に入れて、
前述のg.DrawImage等をe.Graphics.Drawimage等に変えてみると一応ちらつきは収まったものの、
今度は処理がまるで正しく行われなくなりました。
(異常なスピードで動作する、規定した位置に弾が出ない、敵が規定の位置に動かなくなる、タイミングがずれる…等)
■No28007に返信(Kさんの記事)
> 全てPaintで行うよう書き換えようと思ったのですが、どうするかよく判りません。

Sub 何某()  'Timer1_Tick なのかな?
 Dim g As Graphics = Picturebox1.CreateGraphics()
 Dim fnt As New Font("MS UI GOTHIC", font_size)
 Dim color As Brush = font_color
 Dim text As String = "●"
 g.DrawString(text, fnt, color, X, Y)
 fnt.Dispose()
 g.Dispose()
End Sub

という処理を、下記のように書き換えてみてください。

Sub 何某()
 Picturebox1.Invalidate()  '再描画を依頼する
 ' Invalidate / Update / Refresh の違いについては下記参照
 ' http://dobon.net/vb/dotnet/control/refreshupdateinvalidate.html
 ' http://bbs.wankuma.com/index.cgi?mode=al2&namber=55921&KLOG=94
End Sub

Sub Picturebox1_Paint(……
 Using fnt As New Font("MS UI Gothic", font_size)

  '自分で作成した Brush の場合は、使用後には Dispose が必要です。
  '(ただし、SystemBrushes などの場合は扱いが異なります)
  Dim color As Brush = font_color

  Dim text As String = "●"

  'e.Graphics は Dispose してはいけません
  e.Graphics.DrawString(text, fnt, color, X, Y)
 End Using
End Sub
■No28010に返信(魔界の仮面弁士さんの記事)
> Sub Picturebox1_Paint(……
>  Using fnt As New Font("MS UI Gothic", font_size)
>
>   '自分で作成した Brush の場合は、使用後には Dispose が必要です。
>   '(ただし、SystemBrushes などの場合は扱いが異なります)
>   Dim color As Brush = font_color
>
>   Dim text As String = "●"
>
>   'e.Graphics は Dispose してはいけません
>   e.Graphics.DrawString(text, fnt, color, X, Y)
>  End Using
> End Sub

上記のコードを参考に、各サブルーチンから表示に関わる部分だけを抜き出して
Picturebox1_Paintに入れ、e.Graphicsに書き換えるときちんと動きました。ありがとうございました!

ただ、レーザーの処理において、
Dim color As Brush = Brushes.Blue
e.Graphics.FillPolygon(color, points, Drawing2D.FillMode.Alternate)
color.dispose
としたり、Using color〜end usingとすると、「system.argumentexception はハンドルされませんでした」と言われてしまいました。
なので、一応http://jp.bloguru.com/03920/b261030800/systemargumentexceptionを参考に
Disposeの処理を外すととりあえず動きました。
解決済み!
■No28014に返信(Kさんの記事)
> Dim color As Brush = Brushes.Blue
Brushes から得たブラシは Dispose しないでください。
Dispose 対象とすべきなのは、自分で作成・管理しているオブジェクトです。

 Using x As New SolidBrush(Color.Blue)

 End Using


この事は、Graphics や Font や Image などにおいても同様です。

CreateGraphics や Graphics.FromImage で Graphics を生成した場合は
使用後に Dispose する必要がありますが、Paint イベントで得た Graphics は
勝手に Dispose してはいけません。

Font についても、たとえば描画時に Me.Font などを使うのであれば、
それを勝手に Dispose してはいけません。ただし、描画のたびに
New Font(〜〜) するようなケースにおいては、
 Using F As New Font(〜〜)
 End Using
などとして、使用後に Dispose されるようにします。

Image についても、基本的には Dispose が必要なのですが、
まだ PictureBox などに表示中の画像などは、勝手に Dispose しては
いけません。すなわち、
 PictureBox1.Image = tempImage
 tempImage.Dispose()
というコードも NG というわけです。


> e.Graphics.FillPolygon(color, points, Drawing2D.FillMode.Alternate)
ブラシに color という名前を付けると、Color 型と混同しやすくなるので、
動作上は問題無いにしても、別の名前にしておいた方が良いかと思います。
解決済み!
詳しく教えていただいてありがとうございます。
勉強になりました!
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板