101.何もしないプロジェクト

 これから、フルバージョンのサンプルの説明を始めます。このサンプルはFullTutorial001というディレクトリに含まれます。
 BaseCrossDx11.slnというソリューションを開くとDx11版が起動します。

 実行結果は以下のような画面が出ます。濃い青の画面です。

 

図0101a

 


解説

 BaseCrossDx11.slnを開くと、GameSourcesというプロジェクトがあります。これがゲーム本体と言えます。
 このプロジェクトはHeadersというフィルタとSourcesというフィルタに分かれています。
 以下はGameSourcesの内容です。

 

図0101b

 

 サンプルではおおむねファイルはこのような構造になっています。
 それぞれのファイルの役割を簡単に記します。

Scene.h/cpp

 BaseCrossでは、ゲーム上のまとめ役のオブジェクトとしてAppクラスシーンそしてステージという3つ階層で管理します。
 Scene.h/cppはそのシーンです。ここではステージを管理し切り替えます。

GameStage.h/cpp

 これはゲームステージです。シーンにより管理されます。サンプルでは主にゲーム画面を実装しますが、通常はメニュー画面リザルト画面など、複数のステージで構成されます。その場合、メニューなどもGameStage.h/cppに記述することも可能ですが、複数のファイルヘッダとcppのペアを作成し、それぞれ別のステージを管理することも可能です。

Character.h/cpp

 ここには、主にプレイヤー以外のステージに配置されるオブジェクトを記述します。実際のゲームでは、サンプルのように単純ではなく、いろんなタイプのオブジェクトを記述する必要があります。ですので、Character.h/cppのようなヘッダとcppのペアを作成して、そこに記述します。
 例えば敵キャラなどはEnemy.h/cppなどのファイルを記述するとわかりやすいでしょう。

Player.h/cpp

 ここにはプレイヤーを記述します。プレイヤーは、コントローラキーボード、マウスなどの反応してリアルタイムに細かな処理を行う必要があります。当然、ファイルは大きくなります。ですのでCharacter.h/cppとは別に作成します。
 実際のゲームでは必ずしもPlayer.h/cppというファイル名でなくてもかまいません。

ProjectShader.h/cpp

 ここにはコンテンツで定義したシェーダークラスを記述します。シェーダーは特別なファイルです。ライブラリでも基本的なシェーダは実装されてますが、独自に作成する場合.hlslファイルを作成します。このProjectShader.h/cppは、これらのシェーダをゲーム内で使用するために作成する中間的なC++ファイルを記述します。
 シェーダそのもの(.hlslファイル)を作成する場合はフィルタを新しく作成してその中に記述することをお勧めします。

Project.h

 ここにはコンテンツで使用するヘッダをまとめておきます。こうしておくと、コンテンツ内の記述はProject.hをインクルードすればすべてのファイル内に記述されたオブジェクトを使用することができます。
 ただしC++には前方宣言という文法があり、使用するためにはあらかじめ宣言が(場合によっては実体も)必要になります。
 そのためProject.h内は、宣言する順番が重要になります。ポインタや参照を使用するだけなら、クラス宣言や構造体宣言を使用すれば前方宣言は成り立ちますが、それらのメンバ関数呼び出しなどを記述する場合は、Project.h内の順番が上になければなりません。そのあたりを考慮しながら記述します。

WinMain.cpp

 ここにはWindowsアプリケーションのエントリポイントであるWinMain関数を記述します。この内容を書き換えるのは、例えばキーボードやマウスを使用する場合に使用するキーを登録します。
 BaseCrossはコントローラはデフォルトで使用できる形ですが、キーボードやマウスを使用するためにはこの中に記述します。
 WinMain関数すべての始まりですから、この記述を書き換えれば、BaseCrossの枠を簡単に超えられます。
 例えばBaseCrossのライブラリは使用したいが使用方法は独自に管理したいなどの場合は、ここを変更すればシーンやステージあるいはAppクラスの考え方は全く使わずに独自の管理をすることが可能です。

 以上、サンプル内に含まれるファイルの役割でした。

このサンプル独自の内容

 このサンプルは何もしないサンプルです。通常は実際にゲームを作成する場合のひな形として使用します。

 ただ、ゲームエンジン(DirectX)を実装する最低限の処理が実装されています。
 まず、GameStage.hを以下のように記述します。
#pragma once
#include "stdafx.h"

namespace basecross {
    //--------------------------------------------------------------------------------------
    //  ゲームステージクラス
    //--------------------------------------------------------------------------------------
    class GameStage : public Stage {
        //ビューとライトの作成
        void CreateViewLight();
    public:
        //構築と破棄
        GameStage() :Stage() {}
        virtual ~GameStage() {}
        //初期化
        virtual void OnCreate()override;
    };


}
//end basecross
 ここではゲームステージを記述します。ステージメニューステージなどもそうですが、最低限ビューとライトというオブジェクトを実装する必要があります。ここでは、それらを実装する関数void CreateViewLight()関数が宣言されています。
 BaseCrossnamespace basecrossというネームスペースに入れます。こうしておくとほかのグローバルな関数などとクラス名などを分離することができます。
 ここに
        //初期化
        virtual void OnCreate()override;
 という仮想関数があります。このOnなんたらという関数名は、ライブラリから呼び出される関数です。GameStageのOnCreate()関数GameStageのインスタンスが作成されるときに、ライブラリから呼び出されます。
 このような仮想関数はほかにOnUoodate()関数、OnDraw関数などがあります。
 使用する場合はこれらの関数を多重定義します。

   続いてGameStage.cppです。以下のように記述されています。
#include "stdafx.h"
#include "Project.h"

namespace basecross {

    //--------------------------------------------------------------------------------------
    //  ゲームステージクラス実体
    //--------------------------------------------------------------------------------------
    //ビューとライトの作成
    void GameStage::CreateViewLight() {
        auto PtrView = CreateView<SingleView>();
        //ビューのカメラの設定
        auto PtrCamera = PtrView->GetCamera();
        PtrCamera->SetEye(Vec3(0.0f, 5.0f, -5.0f));
        PtrCamera->SetAt(Vec3(0.0f, 0.0f, 0.0f));
        //シングルライトの作成
        auto PtrSingleLight = CreateLight<SingleLight>();
        //ライトの設定
        PtrSingleLight->GetLight().SetPositionToDirectional(-0.25f, 1.0f, -0.25f);
    }
    void GameStage::OnCreate() {
        try {
            //ビューとライトの作成
            CreateViewLight();
        }
        catch (...) {
            throw;
        }
    }

}
//end basecross
 このようにGameStage::CreateViewLight()関数ではビューとライトを設定しています。ビューというのは、ビューポートとカメラを組み合わせたものです。1つのビューポートには1つのカメラが結びついています。複数のビューを使うこともできます。ライトシャドウマップ3Dの陰影をつけるために必要なものです。
 これらの設定を行うGameStage::CreateViewLight()関数GameStage::OnCreate()関数から呼び出します。
 GameStage::OnCreate()関数内にビューやライトの作成を記述してもいいのですが、今後オブジェクトが増えていくと何が何だか分からなくなってしまうので、関連するグループあるいはオブジェクトごとに初期化関数を別に書いたほうが可読性は上がります。

 最後にScene.h/cppを記述します。以下はScene.hです。
#pragma once
#include "stdafx.h"
namespace basecross{
    //--------------------------------------------------------------------------------------
    /// ゲームシーン
    //--------------------------------------------------------------------------------------
    class Scene : public SceneBase{
    public:
        //--------------------------------------------------------------------------------------
        /*!
        @brief コンストラクタ
        */
        //--------------------------------------------------------------------------------------
        Scene() :SceneBase(){}
        //--------------------------------------------------------------------------------------
        /*!
        @brief デストラクタ
        */
        //--------------------------------------------------------------------------------------
        virtual ~Scene(){}
        //--------------------------------------------------------------------------------------
        /*!
        @brief 初期化
        @return なし
        */
        //--------------------------------------------------------------------------------------
        virtual void OnCreate() override;
        //--------------------------------------------------------------------------------------
        /*!
        @brief イベント取得
        @return なし
        */
        //--------------------------------------------------------------------------------------
        virtual void OnEvent(const shared_ptr<Event>& event) override;
    };
}
//end basecross
 ここでは、OnCreate()仮想関数のほかにOnEvent()仮想関数があります。これは、イベントといってオブジェクト同士の指標などのやり取りに使用できる仕組みです。
 なぜ、シーンにこのような仕組みが実装されているかは理由がありますが後ほど説明します。
 まずは、Scene.cppを見てください。以下のようになってます。
#include "stdafx.h"
#include "Project.h"

namespace basecross{
    //--------------------------------------------------------------------------------------
    /// ゲームシーン
    //--------------------------------------------------------------------------------------
    void Scene::OnCreate() {
        try {
            //クリアする色を設定
            Col4 Col;
            Col.set(31.0f / 255.0f,30.0f / 255.0f,71.0f / 255.0f,255.0f / 255.0f);
            SetClearColor(Col);
            //自分自身にイベントを送る
            //これにより各ステージやオブジェクトがCreate時にシーンにアクセスできる
            PostEvent(0.0f, GetThis<ObjectInterface>(), GetThis<Scene>(), L"ToGameStage");
        }
        catch (...) {
            throw;
        }
    }
    void Scene::OnEvent(const shared_ptr<Event>& event) {
        if (event->m_MsgStr == L"ToGameStage") {
            //最初のアクティブステージの設定
            ResetActiveStage<GameStage>();
        }
    }

}
//end basecross
 シーンもステージ同様構築時に呼ばれるのはScene::OnCreate()関数です。
 まず、背景色を濃い青に設定してます。Col4は色を管理するクラスです。内部では、各色、0.0fから1.0fまでの値を持つように設計されています。
 続いては、ここで、ゲームステージをアクティブステージにするという処理は行わずに、
    //自分自身にイベントを送る
    //これにより各ステージやオブジェクトがCreate時にシーンにアクセスできる
    PostEvent(0.0f, GetThis<ObjectInterface>(), GetThis<Scene>(), L"ToGameStage");
 という処理を行っています。PostEvent()関数イベント発行の関数です。ここでは自分自身にイベントを送っています。
 これは、コメントにもあるように、ステージからシーンにアクセスできるようにするためです。
 例えば、ゲーム実行して取得したスコアライフ値など、ゲーム全体で管理する変数は、シーンに置いておいたほうが便利なことが多いです。(どのオブジェクトからもアクセスしやすいので)。
 しかしScene::OnCreate()関数がよばれる時点では、シーンはまだ完全には構築されてないので、そこでステージを構築すると、ステージのOnCreate()関数内ではシーンにアクセスできないのです。
 そのため、いったんシーンの構築を済ませてからステージの構築を行うというテクニックを使用します。
 PostEvent()関数は、そのパラメータはいったんライブラリ内に保持されて、次のターンの最初に指定のオブジェクトに送られます。送られたイベントはOnEvent()仮想関数で受け取ります。シーンの場合はScene::OnEvent()関数です。
 ですので、その中でゲームステージの構築であるResetActiveStage<GameStage>();の呼び出しを行います。
 この関数は、指定の型のステージを構築しその中でステージのOnCreate()を呼び出します。

 しかし、このサンプルのように共有するデータなどがなければ直接構築しても問題はありません。
 その場合はScene.hは以下のように記述します。
#pragma once

#include "stdafx.h"

namespace basecross{

    //--------------------------------------------------------------------------------------
    /// ゲームシーン
    //--------------------------------------------------------------------------------------
    class Scene : public SceneBase{
    public:
        //--------------------------------------------------------------------------------------
        /*!
        @brief コンストラクタ
        */
        //--------------------------------------------------------------------------------------
        Scene() :SceneBase(){}
        //--------------------------------------------------------------------------------------
        /*!
        @brief デストラクタ
        */
        //--------------------------------------------------------------------------------------
        virtual ~Scene(){}
        //--------------------------------------------------------------------------------------
        /*!
        @brief 初期化
        @return なし
        */
        //--------------------------------------------------------------------------------------
        virtual void OnCreate() override;
    };

}
//end basecross
 そしてScene.cppは以下のように構築します。
#include "stdafx.h"
#include "Project.h"

namespace basecross{

    //--------------------------------------------------------------------------------------
    /// ゲームシーン
    //--------------------------------------------------------------------------------------

    void Scene::OnCreate(){
        try {
            //クリアする色を設定
            Col4 Col;
            Col.set(31.0f / 255.0f,30.0f / 255.0f,71.0f / 255.0f,255.0f / 255.0f);
            SetClearColor(Col);
            //最初のアクティブステージの設定
            ResetActiveStage<GameStage>();
        }
        catch (...) {
            throw;
        }
    }



}
//end basecross
 ステージや、ステージに配置するオブジェクトから初期化時にシーンにアクセスする必要がなければこのような記述でも問題ありません。

 この項では、一番単純なBaseCrossアプリケーションの作成方法を紹介しました。
 次項ではもう少し複雑になります。