ここ数日、これを実装しようとして問題が発生しています。私がやろうとしていることに関して同様の質問を広範囲に検索しましたが、私の問題に直接役立つ質問に出くわしていません。
UserControl
基本的に、クラスのグリッドにタイルをレンダリングしています。これは、私が開発している Tile Engine ベースのワールド エディター用です。これは、オープン ワールド ドキュメントといくつかのタイルをブラッシングしたスクリーンショットです。
Bitmap
最初は、ワールドのプレビュー キャンバスとなる を自分のコントロールで使用するつもりでした。たとえば、ブラシ ツールを使用すると、マウスを動かして左ボタンを押したままにすると、カーソルの下にある最も近いタイルがブラシのタイルに設定され、layer
ビットマップにペイントされます。コントロールのメソッドは、ペイント イベントのクリッピング四角形に関してビットマップが描画OnPaint
される場所にオーバーライドされます。layer
この方法の問題点は、大きなワールドを扱う場合、ビットマップが非常に大きくなることです。このアプリケーションは、ワールド サイズに対応できるようにする必要があります。無効化されるたびに大きなビットマップをコントロールにレンダリングすると、パフォーマンスの問題が発生することは明らかです。
現在、コントロールのオーバーライドされたOnPaint
イベントでタイルをコントロールに直接描画しています。多くのメモリを必要としないため、これは素晴らしいことです。たとえば、タイルごとの(1000, 1000)
ワールド(20, 20)
(合計キャンバス サイズは(20000, 20000)
) は、アプリケーション全体で約 18 MB のメモリで実行されます。コントロールが無効化されるたびに、ビューポート内のすべてのタイルを反復処理するため、メモリを集中的に使用するわけではありませんが、かなりプロセッサを集中的に使用します。これにより、非常に厄介なちらつきが発生します。
私が達成したいのは、メモリ使用量とパフォーマンスの点で中間を満たす方法です。コントロールが再描画されるとき (フォームのサイズ変更、フォーカスとぼかし、スクロールなど) にちらつきがないように、基本的に世界をダブル バッファーします。Photoshop を例にとると、開いているドキュメントがコンテナのビューポートからはみ出す場合、どのようにレンダリングするのでしょうか?
OnPaint
参考までに、上記の直接描画方法を使用している私のコントロールのオーバーライドを次に示します。
getRenderBounds
PaintEventArgs.ClipRectangle
世界のすべてのタイルをループして表示されているかどうかを確認する代わりに、表示されているタイルをレンダリングするために使用される四角形を返します。
protected override void OnPaint(PaintEventArgs e)
{
WorldSettings settings = worldSettings();
Rectangle bounds = getRenderBounds(e.ClipRectangle),
drawLocation = new Rectangle(Point.Empty, settings.TileSize);
e.Graphics.InterpolationMode =
System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
e.Graphics.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.None;
e.Graphics.PixelOffsetMode =
System.Drawing.Drawing2D.PixelOffsetMode.None;
e.Graphics.CompositingQuality =
System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
for (int x = bounds.X; x < bounds.Width; x++)
{
for (int y = bounds.Y; y < bounds.Height; y++)
{
if (!inWorld(x, y))
continue;
Tile tile = getTile(x, y);
if (tile == null)
continue;
drawLocation.X = x * settings.TileSize.Width;
drawLocation.Y = y * settings.TileSize.Height;
e.Graphics.DrawImage(img,
drawLocation,
tileRectangle,
GraphicsUnit.Pixel);
}
}
}
私のコードからさらにコンテキストが必要な場合は、コメントしてください。