1. プログラムの構造

この文書では簡単な2Dシューティングゲームの作成を通じて、ゲームアプリケーションの基本的な仕組みや、PSM SDKでの開発手法を説明していきます。

なお、このドキュメントは以下のような方を想定して書かれています。
対象読者
  • ゲームアプリケーションを初めて開発する方。
  • C#の基本的な仕様を理解している方。

最小のPSMプログラム

最初に、最小のPSMプログラムを実行してみましょう。

PSM Studioを起動し、[File] - [Open..] でSample/Tutorialフォルダーを開いてください。

Sample/Tutorialはデフォルトで以下の場所にインストールされています。

  • Windows 7/8の場合: "C:/Users/Public/Documents/Psm/"

sample/Tutorial/Sample01

image/program_guide/explore.PNG

拡張子slnの付いたファイルはソリューションファイルといい、プロジェクトファイルの構成が記述されています。

拡張子csprojの付いたファイルはプロジェクトファイルといい、ソースコード(csファイル)の構成やビルド方法などが記述されています。

アプリケーションを構築するためのSample01フォルダー以下を「プロジェクト」といいます。

※「プロジェクトファイル csproj」と「プロジェクト」は意味が違うので注意してください。

image/program_guide/sln_csproj.PNG

図1 ソリューションファイルとプロジェクトファイルの構成関係

ソリューションファイルがプロジェクトの起点になりますので、アプリケーションの開発を始める時はこのファイルを開いてください。

ソリューションファイルを開いたら、ビルドを行い、F5キーで実行します。

image/program_guide/sample01.PNG

おめでとうございます! ただ、このプログラムは真っ黒な画面が表示されるだけで、他には何も起こりません。 ウィンドウ右上の×ボタンをクリックするか、PSM StudioでShift+F5キーを押してプログラムを終了してください。

PSMアプリケーションの基本的な構造

では、ソースコードをみてみましょう。 ソースコードを見るには、左側のソリューションエクスプローラ内のAppMain.csをダブルクリックしてください。

public class AppMain
{
    static protected GraphicsContext graphics;

    public static void Main (string[] args)
    {
        Initialize ();

        while (true) {
            SystemEvents.CheckEvents();
            Update ();
            Render ();
        }
    }

    public static void Initialize ()
    {
        graphics = new GraphicsContext();
    }

    public static void Update ()
    {

    }

    public static void Render ()
    {
        graphics.Clear();


        graphics.SwapBuffers();

    }
}

ここでは、Main()、Initialize()、SystemEvents.CheckEvents()、Update()、Render()、SwapBuffers()に注目します。

Main()

  • プログラムはまずMain()から始まります。どのアプリケーションでも必ずひとつのMain()関数が必要になります。2つ以上あるとビルドエラーになります。

Initialize()

  • Initialize()の中では主に初期化の処理を記述します。このプログラムの構造では起動時に一度だけこの処理を行います。

while

  • 次にwhileでループに入ります。つまりこの中でSystemEvents.CheckEvents()とUpdate()、Render()を繰り返し処理します。

    SystemEvents.CheckEvents()

    • SystemEvents.CheckEvents ()でOS依存のイベントをチェックします。Windowsではウィンドウメッセージの処理などをここで検知します。

    Update()

    • 主にCPUに任せる計算処理などをUpdate()内に記述します。

    Render()

    • 主に描画処理やGPUに任せる処理などをRender()内に記述します。Render()内では最初にgraphics.Clear()でフレームバッファをクリアします。

    SwapBuffers()

    • SwapBuffers()は垂直同期のタイミングでフレームバッファを切り替えます。

一番最後の「垂直同期のタイミングでフレームバッファを切り替える」ですが、この部分を詳しく説明します。

プログラムがどのように動いているのかを分かりやすくするために、プログラムを以下のように変えてみます。 <-の箇所を追加します。

public class AppMain
{
    static protected GraphicsContext graphics;
    static int colorValue=0;    //<- here.

    public static void Main (string[] args)
    {
        Initialize ();

        while (true) {
            SystemEvents.CheckEvents ();
            Update ();
            Render ();
        }
    }

    public static void Initialize ()
    {
        graphics = new GraphicsContext();
    }

    public static void Update ()
    {
        colorValue++;       //<- here.
        if(colorValue>255)  //<- here.
            colorValue=0;   //<- here.

        graphics.SetClearColor(colorValue, colorValue, colorValue, 255);//<- here.
    }

    public static void Render ()
    {
        graphics.Clear();

        graphics.SwapBuffers();
    }
}

このプログラムを実行すると、画面の背景色がどんどん白くなって、また黒に切り替わります。

image/program_guide/sample01_02.PNG

graphics.SetClearColor(int r, int g, int b, int a)は引数で渡した色で画面をクリアします。

r=255, g=255, b=255は白になります。

aはアルファと呼ばれ、透明の度合いを表します。0で完全な透明、255で完全な不透明になります。

つまりcolorValue++;が何度も繰り返し処理されていることがわかりますね。

この数値はシミュレータでは1秒間に60回更新、つまり約16.6ミリ秒に1度更新されます。

処理の流れ

ダブルバッファ

処理の流れを図で表すと以下のようになります。

./image/program_guide/SwapBuffers.png

ディスプレイに表示するビデオメモリの領域のことを フレームバッファ といいます。

ゲームアプリケーションでは一般的に画面に表示される画像の領域を2つもっており、これをダブルバッファ方式と呼びます。

描画は以下の手順で流れていきます。

  1. まずを後ろの領域(back)を r=1, g=1, b=1でクリアします(上の図では分かりやすくするため、白さを強調しています)。
  2. 描画が完了したら16ミリ秒が経つまで待ちます。
  3. 16ミリ秒が経過したら、r=1, g=1, b=1でクリアした領域を前(front)にします。
  4. 今度は後ろになった領域にr=2, g=2, b=2でクリアします。
  5. 描画が完了したら16ミリ秒が経つまで待ちます。
  6. 16ミリ秒が経過したら、r=2, g=2, b=2でクリア領域を前(front)にします。

この処理を繰り返しながらプログラムが進行していきます。

領域を切り替えて表示するのは、描画途中の画面を表示してしまうと画面が乱れて見えてしまうので、これを避けるためです。

ディスプレイの更新のタイミングに合わせることを「画面の垂直同期を待つ(合わせる)」といいます。

ディスプレイの更新のタイミングが約16.6ミリ秒(注1)なので、フレームバッファの切り替えもこれに合わせた数字になっています。

この一連の処理を行っている箇所が、SwapBuffers()です。

SwapBuffers()で画面の切り替えを待つまでの1コマを 1フレーム と呼びます。

1秒間にフレームを更新する回数を表す単位としてfps(Frame Per Second: フレーム/秒)を用います。 1秒間に60回フレームを更新するとき、60fpsと表記します。

用語の定義

似たような用語がいくつか出てきたので、混乱を避けるために用語の整理をしておきます。

  • 「ディスプレイ」とはハードウェア的な表示装置を指します。
  • 「スクリーン」とはソフトウェア的な画面を指します。
  • 「フレームバッファ」とは、ビデオメモリ上に確保された、スクリーン用の領域を指します。

この章で説明した処理と用語は以降の章でも出てきますので、覚えておきましょう。