Share via


ゲームプログラミング開始

前回は、XNA Game Studio Expressでゲームプロジェクトを新規作成したときに生成されるテンプレートコードの中身の説明をしました。今回は、ゲームプログラムの基本となる以下のものを実装、つまりコーディングをします。

  • コンテントの読み込み
  • SpriteBatchを使ったスプライト表示
  • コントローラーによる移動

今回は、簡単な説明に留めますが、次回からはそれぞれについて詳しく説明していきます。

 

コンテントの読み込み

XNAにはビットマップ、3Dモデル、オーディオといったゲームを作る上で必要なコンテントを簡単に、効率よく使うためのコンテント・パイプラインというものがあります。全てのコンテントは、プロジェクトをビルドした時に、コンテント・パイプラインによって処理され、Windows/Xbox360上で効率良く使えるデータ形式に変換されるようになっています。また、それぞれのゲームで独自形式のフォーマットを取り扱えるようにカスタマイズできる機能を持っています。

今回使うコンテントは、左の飛行機の絵が一枚だけです。XNAでは全ての画像データはテクスチャとして変換されます。コンテントのプロジェクトへの追加はソートコードの追加と同じで、ソリューション エクスプローラからコンテントを追加するプロジェクトを右クリックしてメニューを開き、その中の追加/既存の項目を選びます。 ダイアログボックスが開いたら、ファイルの種類をContent Pipeline Filesにしてから追加したいコンテントを選びます。

GSE 1.0では以下のファイルフォーマット形式がサポートされています。

3Dモデル 2D画像 マテリアル オーディオ
.FBX.X .DDS .BMP.JPG .PNG.TGA .FX .XAP(XACT)

プロジェクトディレクトリ直下に直接コンテントを追加してもいいのですが、コンテントとコードの区別がつくようにプロジェクトディレクトリの下にContentというディレクトリを作っておくと便利です。また、この場合、ContentManagerの初期化の時(Initializeメソッド内)にコンテントのルートディレクトリを指定しておくと、コンテントの読み込みの時に "Content/アセット名" と書かずに済みます。

         // コンテントのルートディレクトリを"Content"に設定する
        content = new ContentManager(Services, "Content");

実際のコンテントの読み込みをする為には以下のようなコードをLoadGraphicsContentメソッド内に書きます。

         // 飛行機用のテクスチャを読み込む
        airPlaneTexture = content.Load<Texture2D>("air-plane");

ContentManagerクラスにはLoad<T> という、ジェネリクスメソッドがあり、どのような型のコンテントでも同じようにして読み込むことができます。.Net 2.0ならではの機能であるジェネリクスを使うことによって、今までのようにLoadTexture2DLoadMeshFromFileといった、長い関数名を覚える必要もなく簡単にコンテントを読み込むことができます。

 

スプライトの描画

XNAでは簡単にテクスチャイメージを描画する為のSpriteBatchというクラスがあります。なぜ単にSpriteではなく、SpriteBatchという名前になっているかというと、このクラスは複数のスプライトをまとめて処理(バッチ処理)する機能をもっているからです。詳細については別の投稿で説明しますが、簡単に言うとWindows/Xbox360の違いを気にせずに、高パフォーマンスを実現しながらも、簡単に使える便利なものといった感じです。

         // SpriteBatchに描画開始を伝える
        spriteBatch.Begin();

        // 飛行機のテクスチャを指定した座標に表示する
        spriteBatch.Draw(airPlaneTexture, airPlanePos, Color.White);

        // SpriteBatchに描画終了を伝える
        spriteBatch.End();

以上のコードをDrawメソッド内に書き加えます。SpriteBatch.BeginSpriteBatch.Endの間にSpriteBatch.Drawメソッドを書くようにします。BeginEndの間には何回でもDrawを呼ぶことができます。SpriteBatch.Beginを呼ぶ前にDrawを呼んだり、SpriteBatch.Beginを続けて呼んだりすると、実行時に例外が発生します。

スプライトは回転、スケールといった機能もサポートしていますが、ここでは単にテクスチャを指定した座標に描画しています。座標を表すのがVector2構造体なので、2D描画に浮動小数点を使って大丈夫なの?と思う人もいるかもしれませんが、最近のGPUの殆どがピクセル以下の描画をサポートしていて、画面をゆっくりとスクロールさせる時などの滑らかさに違いが現れます。

ですから、XNAでゲームを作った場合は、昔の日本製パソコンで出てたシューティングゲームのように 「脅威の0.5ドットスクロール」 と宣伝することが奨励されています(ウソです)。

 

コントローラーによる移動

ここまでで、画面には飛行機が表示されるようになっていますが、ここではその飛行機をコントローラーによって動かすコードを実装します。  

     // 飛行機の座標をコントローラー及び、キーボードの入力によって変更する
    Vector2 dir;
    if (ProcessKeyboardInput(out dir) == false)
    {
        // キー入力が無かったので、コントローラーの左スティックの状態を取得する
        dir = GamePad.GetState(PlayerIndex.One).ThumbSticks.Left;
    }

    dir.Y = -dir.Y; // スクリーン座標のY方向とキー入力の上下を合わせる

    const float speed = 400.0f; // ピクセル/秒の速度
    airPlanePos += dir * (float)gameTime.ElapsedGameTime.TotalSeconds * speed;

 上のコードをUpdateメソッド内に書き加えます。日本では北米程にXbox360が普及していない、つまりXbox360コントローラーを持っている人が少ないと思われるので、ProcessKeyboardInputというメソッドでキーボード入力をサポートしましたが、ここでは割愛します。

ゲームコントローラーの入力は、自由な時にGamePad.GeStateを使うことによって、その状態を知ることができます。コントローラーが繋がっていない場合でも、プログラム自体は問題なく動作するので上のように非常に簡単にコントローラーの入力を処理することができます。

ここではThumbSticks.Leftという、コントローラーの左スティックの状態を取得しています。ThumbSticks.LeftVector2構造体で、スティックの左右はVector2.Xに格納され、左が-1、右の状態が1の値を示し、スティックの上下はVector2.Yに格納され、上が1、下が-1の値を示します。アナログスティックなので傾け方によって、-1から1までの値に変化します。

コントローラーから、移動したい方向を取得した後は、その方向への移動量を現在の座標に加えることによって移動します。ここでは単に一定量の速度で移動させるのではなく、GameTime.ElapsedGameTimeを使うことで、Game.IsFixedTimeStepfalseの時にフレームレートが変化した場合でも、飛行機が同じ速度で移動するようになっています。Game.IsFixedTimeStepによるゲーム更新の仕方の違いは次の投稿で詳しく説明します。

 

ゲームの基本ができた

ここまででゲーム製作の基本である、コンテントの読み込み、メインループでの更新、描画、そしてコントローラーの入力といった処理のXNA上でのやり方を紹介してきました。これだけでも、簡単な2Dベースのゲームを作る下地はできたのではないでしょうか?

今回、実装したコードをアップしておきます。下に表示されているAttachmentのSample01.zipをクリックすることでダウンロードできます。Windows用とXbox360用のソリューションファイルが入っているので、どちらのプラットフォームでも動作します。

Sample01.zip