WisdomSoft - for your serial experiences.

デバイス管理

ゲーム画面の描画には、描画命令を受け付けるグラフィックスデバイスが必要となります。XNA Framework では GraphicsDeviceManager クラスがデバイスの選択や初期化を行ってくれます。

描画の準備

ここまでのプログラムで、ゲーム用のウィンドウを表示し、ゲームに関連する基本的なオブジェクトの生成などの初期化処理、実行の流れなどを紹介しました。ゲームで最も重要な、グラフィックスの表示を早く試したいかもしれませんが、あと少しだけ辛抱してください。多くのプログラミングの入門は "Hello world!" という簡単なテキストを表示するところから始まることで有名ですが、XNA Framework でテキストを表示するのは簡単ではないのです。描画するデバイスを適切に取得し、フォントを読み込み、正しい手順でテキストを描画しなければなりません。そのためには、ゲームの実行手順を正しく理解する必要があります。

ゲーム画面の描画を開始するには、最初にグラフィックスデバイスを初期化しなければなりません。グラフィックスデバイスは、ゲーム画面を描画する機能を提供します。画像などのグラフィックスに関連したリソースの読み込みにも必要となるため、ゲームの描画処理の中心的役割を担います。明示的にグラフィックスデバイスを選択することも可能ですが、通常は、グラフィックスデバイスの管理を行ってくれる Microsoft.Xna.Framework.GraphicsDeviceManager クラスを使います。

Microsoft.Xna.Framework.GraphicsDeviceManager クラス
public class GraphicsDeviceManager : IGraphicsDeviceService, IDisposable, IGraphicsDeviceManager

このクラスを使うことで、グラフィックデバイスの煩雑な初期化や管理を省略できます。GraphicsDeviceManager は、関連付けられているゲームのためのグラフィックデバイスを作成し、管理してくれます。私立ちゲーム開発者は、生成した GraphicsDeviceManager オブジェクトが提供するグラフィックデバイスを利用するだけで、基本的な管理を委ねることができます。GraphicsDeviceManager なしでグラフィックデバイスを構築することも可能ですが、そのためには XNA Framework の基本的な構造を理解する必要があります。

まずは、ゲームを開始する前に GraphicsDeviceManager クラスのインスタンスを作成します。通常、この処理はゲームのコンストラクタ内で行います。 

GraphicsDeviceManager クラスのコンストラクタ
public GraphicsDeviceManager (Game game)

game パラメータには、この GraphicsDeviceManager に関連付けるゲームを指定します。GraphicsDeviceManager は、パラメータに指定されたゲームのグラフィックデバイスを管理するように、登録します。

デフォルトの設定でグラフィックデバイスを生成する場合、GraphicsDeviceManager オブジェクトに対して特別な操作は必要ありません。関連付けているゲームが Run() メソッドによって起動すると、GraphicsDeviceManager は自動的にグラフィックデバイスを生成します。

生成されたグラフィックデバイスは、GraphicsDeviceManager クラスの GraphicsDevice プロパティ、または Game クラスの GraphicsDevice プロパティから取得できます。通常は Game クラスの GraphicusDevice プロパティを利用することになるでしょう。

Game クラス GraphicsDevice プロパティ
public GraphicsDevice GraphicsDevice { get; }

ゲームが起動され、GraphicsDeviceManager によってグラフィックデバイスが生成されている場合、GraphicsDevice プロパティは、適切な Microsoft.Xna.Framework.Graphics.GraphicsDevice クラスのオブジェクトを返します。この GraphicsDevice が、描画処理に必要なグラフィックデバイスそのものを表します。

Microsoft.Xna.Framework.Graphics.GraphicsDevice クラス
public class GraphicsDevice : IDisposable

GraphicsDeviceManager インスタンスが生成された時点では、グラフィックデバイスは有効ではないので注意してください。ゲームが Run() メソッドによって起動され、適切な初期化処理が行われなければ GraphicsDevice は生成されません。GraphicsDevice は、ゲームを起動した後、Initialize() メソッドが呼び出される前に生成されます。

GraphicsDevice が GraphicsDeviceManager によって生成されると DeviceCreated イベントが発生します。

GraphicsDeviceManager クラス DeviceCreated イベント
public event EventHandler<EventArgs> DeviceCreated

グラフィックデバイスが生成されたタイミングで確実に実行しておきたい初期化処理などがある場合は、このイベントを使うと便利でしょう。

コード1
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

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.DeviceCreated += (sender, e) => Debug.WriteLine("Device created");
        Debug.WriteLine("End constructor");
    }

    protected override void Initialize()
    {
        Debug.WriteLine("Initialize");
        base.Initialize();
    }
}
実行結果
コード1 実行結果

コード1は、コンストラクタ内で GraphicsDeviceManager クラスのインスタンスを生成し、フィールドにオブジェクトを保存しています。このとき、DeviceCreated イベントに匿名メソッドを登録し、デバイスが生成されたタイミングで文字列を出力するように設定しています。結果から、コンストラクタを終了したあと、Initialize() メソッドが始まる前の間に GraphicsDeviceManager によって GraphicsDevice が生成されていることが確認できます。Run() メソッドを呼び出すと、Game クラスが Initialize() メソッドを呼び出す前に、自分に関連付けられている GraphicsDeviceManager に GraphicsDevice を生成するように命じています。

これで、GraphicsDevice を生成する工程は終了です。

コンテンツの読み込み

GraphicsDevice が正常に構築されると、コンテンツの読み込みを行うための LoadContent() メソッドが呼び出されます。画像やフォントなど、ゲーム内で利用するリソースは、このメソッドをオーバーライドして読み込みます。通常、このメソッドはゲームの初期化の過程で呼び出されます。

Game クラス LoadContent() メソッド
protected virtual void LoadContent ()

LoadContent() メソッドをオーバーライドし、このメソッドの中で必要な外部データを読み込みます。XNA Framework でのコンテンツの扱いは特殊なので、コンテンツ管理の詳細は後述します。この場では、ゲームが開始されてからの手順をご理解いただければ十分です。

ゲーム終了時には、LoadContent() メソッドに対応する UnloadContent() メソッドが呼び出されます。通常、LoadContent() メソッドで読み込んだデータは UnloadContent() メソッドで解放します。 

Game クラス UnloadContent() メソッド
protected virtual void UnloadContent ()

ゲームで画像などを読み込むときには、必ず GraphicsDevice オブジェクトが必要になります。どこで画像などの外部リソースを読み込むかについて制限はありませんが、これらのメソッドが呼び出されたタイミングで必要なデータを読み込む方法が一般的です。

コード2
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);
    }

    protected override void Initialize()
    {
        Debug.WriteLine("Initialize");
        base.Initialize();
    }
    protected override void LoadContent()
    {
        Debug.WriteLine("LoadContent");
        base.LoadContent();
    }
    protected override void BeginRun()
    {
        Debug.WriteLine("BeginRun");
        base.BeginRun();
    }

    protected override void EndRun()
    {
        Debug.WriteLine("EndRun");
        base.EndRun();
    }
    protected override void UnloadContent()
    {
        Debug.WriteLine("UnloadContent");
        base.UnloadContent();
    }
}
実行結果
コード2 実行結果

コード2は、LoadContent() メソッドと UnloadContent() メソッドをオーバーライドし、これらのメソッドが適切に呼び出されているかどうかを調べるプログラムです。Debug クラスを使って出力した結果を見ると、Initialize() メソッドが実行された後に LoadContent() メソッドが実行されていることが確認できます。

デバイスのリセット

一般的に Xbox 360 や Windows Phone 7 といった専用機であればグラフィックスデバイスはゲーム中に常に固定されますが、Windows PC で実行している場合、状況に応じてグラフィックデバイスが失われ、再生成されることがあります。例えば、色数や解像度といった設定の変更や、マルチディスプレイ環境でゲームのウィンドウを別のディスプレイに移動した場合です。この時、ゲーム画面を描画するデバイスが切り替わるため、 GraphicsDeviceManager クラスによって生成された GraphicsDevice オブジェクトが無効になります。

デバイスがリセットされると GraphicsDeviceManager クラスの DeviceResetting イベントDeviceReset イベントが発生します。

GraphicsDeviceManager クラス DeviceResetting イベント
public event EventHandler<EventArgs> DeviceResetting
GraphicsDeviceManager クラス DeviceReset イベント
public event EventHandler<EventArgs> DeviceReset

DeviceResetting イベントは、現在有効な GraphicsDevice オブジェクトがリセットされそうになると発生し、DeviceReset イベントは GraphicsDevice オブジェクトがリセットされ、新しいグラフィックスデバイスを使用する準備が整った時点で発生します。

初期の XNA Framework 1.0 では、デバイスがリセットされると、それまでの GraphicsDevice インスタンスは無効となり、新しいインスタンスが設定され DeviceCreated イベントが発生していました。現在ではリセットが発生してもインスタンスは同じまま(デバイスの切り替えは内部に隠ぺいされている)なので、設定だけが初期化されます。従って DeviceCreated イベントが発生することはありません。

コード3
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

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.DeviceCreated += (sender, e) => Debug.WriteLine("Device created");
        graphicsDeviceManager.DeviceResetting += (sender, e) => Debug.WriteLine("Device resetting");
        graphicsDeviceManager.DeviceReset += (sender, e) => Debug.WriteLine("Device reset");
    }
}
実行結果
コード3 実行結果

コード3の実行結果はマルチディスプレイ環境でゲームウィンドウを別のディスプレイに移動させ、意図的にデバイスリセットを発生させたものです。ウィンドウが移動元ディスプレイから外れるときに DeviceResetting イベントが発生し、移動先のディスプレイに移った時に DeviceReset イベントが発生します。通常、DeviceResetting イベントと DeviceReset イベントは、連続して発生します。

デバイスがリセットされると GraphicsDevice オブジェクトの設定が初期化されるため、必要に応じて上記の方法でデバイスのリセットを感知して再設定しなければなりません。ゲームによっては、グラフィックスデバイスの性能などを調べなおすといった工程も必要かもしれません。