新しいゲームの生成と起動

今回は XNA Framework ゲームが起動するまでの仕組みと、Windows 用ゲームのためのウィンドウ制御について説明します。

ゼロからはじめるXNAプログラミング - C#の自作ゲームがXbox 360でも動く「XNA Game Studio」

メニューを追加した状態

XNA Framework ゲームも、他の一般的な .NET Framework アプリケーションと同様に Main() メソッドから起動します。通常 Main() メソッドでは、ゲームそのものを表す Microsoft.Xna.Framework.Game クラスのインスタンスを生成し、ゲームを起動させます。Game クラスは、なにも画面を表示しない空のゲームを表すクラスで、新しいゲームを作るにはこのクラスを継承して必要なメソッドをオーバーライドしていきます。仕組みとしては、Windows フォームにおける Form クラスを継承して GUI をデザインする工程に似ています。

Microsoft.Xna.Framework.Game クラス

public class Game : IDisposable

Game クラスは IDisposable インタフェースを実装するリソースの一種です。従って、インスタンスが不要になったら Dispose() メソッドを呼び出して解放してください。通常は using 文を使います。

ゲームを起動するには、Game オブジェクトの Run() メソッドを呼び出してください。このメソッドが呼び出されると、ゲームの初期化が行われ、その後はゲームの更新や描画処理を繰り返すゲームループに移行します。ゲームループは、Windows アプリケーションにおけるメッセージループのようなものだと考えてください。ゲームが終了するまで、Run() メソッドは制御を返しません。

Game クラス Run() メソッド

public void Run ()

ゲームを明示的に終了させるには Exit() メソッドを呼び出します。Windows ゲームの場合、表示されているウィンドウの「閉じる」ボタンが押されるとゲームを終了します。Xbox 360 ゲームにはウィンドウが存在しないので、通常はタイトル画面などに「ゲームを終了する」などの項目を設置して、ユーザーが選択したタイミングで Exit() メソッドを呼び出します。

Game クラス Exit() メソッド

public void Exit ()

ゲームが終了するときに呼び出される Exiting イベントや、Dispose() メソッドによって破棄されようとしたときに呼び出される Disposed イベントなども Game クラスで提供されています。

最初に、画面には何も表示しない単純なゲームを作成してみましょう。通常は、Game クラスを継承した新しいクラスを用意しますが、この場では単純に Game クラスをインスタンス化して表示します。

コードを実行するには

Visual C# 2008 Express Editonで新規プロジェクトを作成した後(参照:ゼロからはじめるXNAプログラミング - C#の自作ゲームがXbox 360でも動く「XNA Game Studio」)、自動生成されたProgram.csとGame1.csを削除、[プロジェクト]メニューの[クラスの追加]で表示される「新しい項目の追加」からコードファイルを選択し(図A)、Test.csの名前でコードを追加し、そこにコードを記述し実行させてください(図B)。

図A.自動生成されたProgram.csとGame1.csを削除、[プロジェクト]メニューの[クラスの追加]でコードファイルを作成

図B.作成したTest.csにサンプルのコードを記述して実行

サンプル01

using Microsoft.Xna.Framework;

class Test
{
    static void Main(string[] args)
    {
        using (Game game = new Game())
            game.Run();
    }
}

実行結果

サンプル01 実行結果

Game クラスのインスタンスは、何も表示しない空のゲームなのでサンプル 01 の実行結果は単純なウィンドウが表示されるだけです。ゲーム画面の初期化を行っていないため、ウィンドウの内部は実装依存となるでしょう。ウィンドウのサイズは、ゲーム画面(バックバッファ)のサイズによって決定されます。デフォルトでは 800 × 600 のゲーム画面が作られるため、これにタイトルバーや境界線を含んだウィンドウサイズとなります。

ゲームウィンドウの制御

Xbox 360 の場合、ウィンドウを持たないためウィンドウを制御することはできません。しかし、Windows の場合はウィンドウが表示されるため、ウィンドウサイズの取得や変更が可能です。ただし、一般的な Windows アプリケーションのような汎用的な機能が提供されるものではなく、ゲームに必要な最小限のウィンドウ操作しかできません。

実行しているゲームのウィンドウは Game オブジェクトの Windows プロパティから取得します。

Game クラス Window プロパティ

public GameWindow Window { get; }

ウィンドウは GameWindow クラスとして表されています。このクラスのオブジェクトは、ゲームに関連付けられているシステムのウィンドウを表します。Xbox 360 用のゲームでも利用できますが、多くの機能は無視されます。

Microsoft.Xna.Framework.GameWindow クラス

public abstract class GameWindow

代表的なウィンドウの利用方法は、ゲームのタイトルや状態をウィンドウのタイトルバーに表示することでしょう。ウィンドウのタイトルは Title プロパティから設定・取得できます。

GameWindow クラス Title プロパティ

public string Title { get; set; }

このプロパティに設定した文字列が、ウィンドウのタイトルバーに表示されるテキストとなります。

サンプル02

using Microsoft.Xna.Framework;

class Test
{
    static void Main(string[] args)
    {
        using (Game game = new Game())
        {
            game.Window.Title = "ホイホイ☆チャーハン - You got me mad now.";
            game.Run();
        }
    }
}

実行結果

サンプル02 実行結果

デフォルトの設定では、ウィンドウのサイズは固定です。業務やツール系のアプリケーションとは異なり、ゲームのウィンドウは自由にサイズが変更されることを望まないでしょう。画面のアスペクト比やレイアウトが崩れ、余計な伸縮処理に負荷もかかります。しかし、ゲームの種類によってはウィンドウサイズの自由な変更が問題にならないこともあるでしょう。

一般的なアプリケーションのようにウィンドウサイズを自由に変更できるようにするに AllowUserResizing プロパティの値を true に変更します。デフォルトでは false が設定されています。

GameWindow クラス AllowUserResizing プロパティ

[DefaultValueAttribute(false)]
public abstract bool AllowUserResizing { get; set; }

現在のウィンドウサイズは ClientBounds プロパティから取得できます。このプロパティは、長方形領域の座標とサイズを提供する Rectangle 構造体の値を返します。

GameWindow クラス ClientBounds プロパティ

public abstract Rectangle ClientBounds { get; }

ウィンドウのサイズがユーザーの操作などによって変更されると ClientSizeChanged イベントが発生します。

GameWindow クラス ClientSizeChanged イベント

public event EventHandler ClientSizeChanged

このイベントにデリゲートを登録することで、ウィンドウサイズの変更を追跡できます。

サンプル03

using Microsoft.Xna.Framework;

class Test
{
    static void Main(string[] args)
    {
        using (Game game = new Game())
        {
            GameWindow wnd = game.Window;
            wnd.AllowUserResizing = true;
            wnd.ClientSizeChanged += (sender, e) => { wnd.Title = wnd.ClientBounds.ToString(); };
            game.Run();
        }
    }
}

実行結果

サンプル03 実行結果

このプログラムはウィンドウのサイズを変更すると ClientSizeChanged イベントに登録したデリゲートが呼び出され、新しいウィンドウのサイズがタイトルバーに表示されるというものです。

Windows フォームとの組み合わせ

Xbox 360 との互換性を重視する場合は利用するべきではありませんが、Windows 用のゲームであればメニューバーなど Windows 固有の機能を使いたいと考えるかもしれません。XNA Framework では Windows 固有の機能は提供されませんが、.NET Framework の機能をすべてい利用できるので、例えばファイルアクセスやネットワーク通信も自由にできます。

ウィンドウ上にコントロールを配置するなど、GameWindow クラスがサポートしていない機能を利用したい場合は Windows フォームなどの機能と組み合わせます。例えばメニューバーを追加したければ、GameWindow オブジェクトから Form オブジェクトを生成します。

GameWindow クラスでは、ウィンドウのハンドルを提供する Handle プロパティが提供されています。このプロパティから得られる値は Windows API における HWND 型の値に等しいもので、ネイティブシステムのウィンドウとして識別できます。この値から、Windows フォームの Control クラスで提供される静的な FromHandle() メソッドを使って Form オブジェクトに変換できます。

GameWindow クラス Handle プロパティ

public abstract IntPtr Handle { get; }

ここから先は、Windows フォームの機能を使って自由にウィンドウを制御できます。Windows フォーム以外にも WPF やネイティブコードとの相互運用も検討できます。

Windows フォームを利用する場合は「プロジェクト」メニューの「参照の追加」を選択して「System.Windows.Forms」を選択してください。

サンプル04

using System.Windows.Forms;
using Microsoft.Xna.Framework;

class Test
{
    static void Main(string[] args)
    {
        using (Game game = new Game())
        {
            Form form = (Form)Control.FromHandle(game.Window.Handle);

            MenuItem exitMenu = new MenuItem("終了");
            MenuItem fileMenu = new MenuItem("ファイル");
            MainMenu menu = new MainMenu();

            exitMenu.Click += (sender, e) => { game.Exit(); };
            fileMenu.MenuItems.Add(exitMenu);
            menu.MenuItems.Add(fileMenu);
            form.Menu = menu;

            game.Run();
        }
    }
}

実行結果

サンプル04 実行結果

サンプル 04 は、ゲームウィンドウにメニューバーを追加して「ファイル」メニューの「終了」項目からゲームを終了できるようにしています。メニュー項目を表す MenuItem クラスや、メニューバーを表す MainMenu クラスなどは、すべて Windows フォームのものです。XNA Framework の機能ではないため、当然 Xbox 360 では利用できません。

マウスカーソルの表示

通常、Xbox 360 ゲームであれば Xbox 360 専用コントローラを使って操作されることを前提に開発しますが、Windows 用のゲームの操作はマウスとキーボードの組み合わせが一般的です。USB の有線コントローラであれば Windows でも利用できるため、実質的に Windows 用のゲームはコントローラ、マウス、キーボードの入力に対応します。しかし、Xbox 360 ではマウスを使えません。

表01 入力機器の対応

対応 コントローラ キーボード マウス
Windows ゲーム
Xbox 360 ゲーム ×

デフォルトの設定では、ゲーム画面上でのマウスカーソルの表示がオフになっています。よって、ウィンドウ上にマウスカーソルを移動させるとマウスカーソルが消えてしまいます。Windows ゲームでマウスカーソルを表示させるには Game オブジェクトの IsMouseVisible プロパティを true に変更してください。当然、マウスがサポートされない Xbox 360 ゲームでは機能しません。

Game クラス IsMouseVisible プロパティ

public bool IsMouseVisible { get; set; }

マウスを利用する場合でもゲーム独自のカーソルアイコンを利用する場合は、このプロパティを false に設定してゲーム内で描画する必要があります。

サンプル05

using Microsoft.Xna.Framework;

class Test
{
    static void Main(string[] args)
    {
        using (Game game = new Game())
        {
            game.IsMouseVisible = true;
            game.Run();
        }
    }
}

実行結果

サンプル05 実行結果

上記のサンプルはゲームを起動する前に IsMouseVisible を true に設定しているため、ゲーム画面上でもシステムのマウスカーソルが表示されます。

XNA Framework を使えば、コードの大部分を Windows と Xbox 360 で共有できますが、このようにウィンドウやシステムに関連する一部の機能はプラットフォーム固有となるので注意してください。