フレームの計測
描画パフォーマンス
ゲームの実行環境のパフォーマンスの主要な指標に fps (Frame Per Second) があります。fps は、1 秒間の間に描画したゲーム画面の枚数のことを表すもので、XNA Framework では Draw() メソッドが 1 秒間の間に呼び出された回数に相当します。ゲームに限らず、動画などのマルチメディア関連のベンチマークで重要視される値です。一定以上の fps が安定して計測されていれば、ゲーム画面はなめらかに動いて見えます。逆に、fps が低ければ、映像はカクカクした不自然な動きに見えます。
ゲーム開発者の目標は、常に 60 fps 以上、すなわち 1 秒間の間に 60 回以上 Update() メソッドと Draw() メソッド呼び出せるパフォーマンスを維持し続けることです。この 60 fps は、XNA Framework が既定で設定している固定ステップに一致します。ゲームのために書かれるコードのうち、パフォーマンスに重大な影響を与えるのはゲームループ内で実行されるコードです。ゲームは、1 フレームを構築する工程で、データ処理、描画、入力処理、サウンド、ネットワーク、人工知能などの処理を実行します。これを 1 秒間に 60 回実行できるパフォーマンスを維持する必要があるのです。
コードを最適化するタイミングは開発者のセンスですが、ゲームが目標のパフォーマンスを維持しているかどうかは、常に意識するべきです。そのためにはベンチマークが必要です。固定ステップのゲームで fps を計測するには、ゲーム時間ではなく、現実の時間で 1 秒間の Draw() メソッドの呼び出し回数を観測する必要があります。
固定ステップの場合、パフォーマンスに余裕があっても設定されている上限以上の fps は出ることはないので注意てください。最大 fps をベンチマークとして計測したい場合は、可変ステップにする必要があります。ただし、GraphicsDeviceManager をインスタンス化し GraphicsDevice オブジェクトを保有している場合、可変ステップでも fps の上限が固定されるので注意してください。これは、GraphicsDeviceManager に設定されている SynchronizeWithVerticalRetrace プロパティが影響しています。
public bool SynchronizeWithVerticalRetrace { get; set; }
これは、垂直同期 (V-Sync) と呼ばれるもので、GraphicsDevice オブジェクトが指すディスプレイが 1 秒間に画面を書き換える回数に Draw() メソッドの呼び出しを同期させる機能です。
ディスプレイが画面を更新する速度は、リフレッシューレートと呼ばれています。垂直同期が有効の場合 Draw() メソッドを呼び出したあと、次の画面更新まで垂直同期待ちが発生します。このプロパティが true の場合、ゲームはリフレッシュレートに合わせて描画を行います。ベンチマークで、実行環境の最大 fps を調べるには、可変ステップにすることに加え、垂直同期を行わないように SynchronizeWithVerticalRetrace プロパティに false を設定する必要があります。
using System; using System.Diagnostics; using Microsoft.Xna.Framework; public class TestGame : Game { public static void Main(string[] args) { using (Game game = new TestGame()) game.Run(); } private GraphicsDeviceManager graphicsDeviceManager; public TestGame() { graphicsDeviceManager = new GraphicsDeviceManager(this); graphicsDeviceManager.SynchronizeWithVerticalRetrace = false; IsFixedTimeStep = false; } private DateTime prevTime; private int framePerSec; protected override void Initialize() { prevTime = DateTime.Now; base.Initialize(); } protected override void Draw(GameTime gameTime) { framePerSec++; DateTime now = DateTime.Now; TimeSpan t = now - prevTime; if (t.TotalMilliseconds >= 1000) { Window.Title = framePerSec + "fps"; Debug.WriteLine(framePerSec + "fps"); framePerSec = 0; prevTime = now; } base.Draw(gameTime); } }
コード1は垂直同期を無効にした可変ステップのゲームで、可能な限り Update() メソッドと Draw() メソッドを呼び出し続けるように設定しています。Draw() メソッドでは、framePerSec フィールドの値をインクリメントし、Draw() メソッドが呼び出された回数をカウントします。
直前の計測時間を prevTime フィールドに保存し、現在時刻と prevTime フィールドの差が 1000 ミリ秒を超えた時点で、それまでカウントした framePerSec フィールドの値をタイトルバーに表示します。こうすることで、常に最新の 1 秒間の実フレーム数を表示できます。