Simplestar Game

How do I create a 3D game ?
http://simplestar.syuriken.jp/
since 17/01/2009
  • BACK
  • HOME
  • NEXT
  • |

:::::::::::032話 ライブラリの作り方:::::::::::

こんにちは、星姫です。
コンソールアプリのmain関数くらいしか開発の経験がない皆さんは、ライブラリと聞くと
どうやって作るんだろう?って首をかしげると思います。
今回はライブラリの作り方から、丁寧に解説していこうと思います。

というわけで、DirectXをもっと簡単に扱えるようなクラス群を提供するライブラリを作成してみましょう。

ライブラリを作る意味、使う意味って何なんでしょうね。
C++言語を使い始めて3年目に入りましたが、実際にライブラリをメンテナンスしたのは、ついこの前のことでした。
巨大なプログラムを書くようになると必要になるのかな?
正直、興味本位でライブラリに手を出してみようと思ったのが今回のレッスンの始まりです。

さて、ここから本題であるライブラリの作り方です。

ライブラリには2種類あります、スタティックライブラリとダイナミックリンクライブラリ(DLL)です。
どちらも、正体は複数の関数やクラス等の機能群をまとめたファイルのことです。

では、さっそく作り方ですが、次のMSDNのドキュメントを読めば、実は誰でも作れるようになります。

スタティック ライブラリ (LIB: Static Library) の作成と使用

ダイナミック リンク ライブラリ (DLL: Dynamic Link Library) の作成と使用

ハイ!ということで、みなさんはライブラリを作成することができるようになりました。
(丁寧な解説どころか、講座の意味が無い気が…まぁ出来るようになればよいか。)

ではさっそく、DirectX を簡単に扱えるクラスというものを考えていきましょうか。

ゲームを作る時、最初に考えるのが表示するウィンドウサイズです。
ゲーム中に任意のタイミングで画面解像度を変更でき、フルスクリーンモードにしたり、ウィンドウモードにできると良いよね。
ということで、レンダリングターゲットをいつでも変更できる次のような機能を作ることにします。

レンダリングターゲットの設定(画面解像度、フルスクリーンかどうか)

次に、とても基本的な機能である、三角形を描画する機能を用意します。
三角形を描画するには

@マテリアルごとのエフェクトを用意する(ディスクからシステムメモリに読み込み、ビデオメモリに転送しておく)
Aマテリアルごとのテクスチャ(リソース)を用意する(ディスクからシステムメモリに読み込み、ビデオメモリに転送しておく)
B頂点バッファを用意する(ディスクからシステムメモリに読み込み、ビデオメモリに転送しておく)
Cインデックスバッファを用意する(ディスクからシステムメモリに読み込み、ビデオメモリに転送しておく)
D頂点レイアウトを用意する(ディスクからシステムメモリに読み込み、ビデオメモリに転送しておく)
E適用するエフェクトの変数を設定する
F適用するテクニックを設定する
G適用するテクニックの頂点レイアウトを設定する
Hインデックスバッファのオフセット(開始位置)とインデックスの数(三角形なら3個)を設定する

必要があります。(三角形1個描画するにしてもやることは多いですね…)
となると、描画用に次のような機能を作ればよいことがわかります。(カッコ)の中身は引数です。

エフェクトの追加(エフェクトが書かれた.fxファイルパス)
エフェクトの削除(削除するインデックス、またはエフェクトファイルパス)
エフェクトリストの解放()
エフェクトの取得(エフェクトのインデックス)
リソースの追加(リソースファイルパス)
リソースの削除(削除するインデックス、またはリソースファイルパス)
リソースリストの解放()
リソースの取得(リソースのインデックス)
頂点バッファの作成(バッファサイズ、システムメモリのバッファポインタ)
頂点バッファの解放()
インデックスバッファの作成(バッファサイズ、システムメモリのバッファのポインタ)
インデックスバッファの解放()
頂点レイアウトの作成(頂点レイアウトフォーマット、エフェクトのインデックス、テクニックのインデックス)
頂点レイアウトリストの解放()
メッシュの描画(インデックスバッファのオフセットとインデックス数と頂点バッファのストライド、エフェクトのインデックス、テクニックのインデックス)

あとは、カメラの位置とプロジェクション設定ができれば、描画に必要な条件が整います。
これを設定できる次の機能を付けておきましょう。
(よくよく考えてみれば、これらはエフェクトの変数になるので、設定関数ではなく取得関数ということで、目的の行列を返すようにしました。)

カメラの設定(カメラ台の姿勢、注視点)
プロジェクションの設定(画角)

最後に、バックバッファとフロントバッファを切り替える機能を付け加えて完成です。

このほかにも、描画前にレンダリングターゲットと深度ステンシルビューをクリアする機能も必要でした。
なので、次の機能も付け加えておきましょう。

画面のクリア()

こんなものですね。ということで、次に列挙する機能を持つライブラリを作ってみましょう。

        //------------------------------------------------------------------------
        // Public Functions
        //------------------------------------------------------------------------
    public:
        // インスタンスの取得
        static SimpleDX10& Instance(void);
        // ウィンドウプロシージャ
        static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

        // レンダリングターゲットの設定
        ErrorCode_SetRenderTarget        SetRenderTarget( RESOLUTION_TYPE& resolution, bool bFllScreen = false,
            HINSTANCE hInstance = NULL, int nCmdShow = SW_SHOWNORMAL);
       
        // エフェクトの追加
        bool    AppendEffect(const char* pFilePath, unsigned int& outIndex);
        // エフェクトの削除 - インデックス版
        void    EraseEffectByIndex(unsigned int index);
        // エフェクトの削除 - 追加時のファイルパス版
        void    EraseEffectByFilePath(const char* pFilePath);
        // エフェクトリストの解放
        void    ReleaseEffects(void);
        // エフェクトを取得する
        ID3D10Effect*    GetEffect(unsigned int index, ID3D10Effect*& pOutEffect) const;

        // リソースの追加
        bool    AppendResource(const char* pFilePath, unsigned int& outIndex);
        // リソースの削除 - インデックス版
        void    EraseResourceByIndex(unsigned int index);
        // リソースの削除 - 追加時のファイルパス版
        void    EraseResourceByFilePath(const char* pFilePath);
        // リソースリストの解放
        void    ReleaseResources(void);
        // リソースを取得する
        ID3D10ShaderResourceView*    GetResource(unsigned int index, ID3D10ShaderResourceView*& pOutResource) const;

        // 頂点バッファの作成
        bool    CreateVertexBuffer(unsigned int bufferSize, const void* pBuffer);
        // 頂点バッファの解放
        void    ReleaseVertexBuffer(void);
        // インデックスバッファの作成
        bool    CreateIndexBuffer(unsigned int bufferSize, const void* pBuffer);
        // インデックスバッファの解放
        void    ReleaseIndexBuffer(void);
        // 頂点レイアウトの作成
        bool    CreateVertexLayout(SMPLDX10_VERTEX_FORMAT format, unsigned int effectIndex, unsigned int techniqueIndex);
        // 頂点レイアウトの解放
        void    ReleaseVertexLayout(void);

        // カメラの設定
        void    SetCamera(D3DXMATRIX& matWorld, D3DXVECTOR3& vecAt);
        // 画角の設定
        void    SetProjection(float angle);
        // レンダリングターゲットのクリア
        void    ClearTargetView(void);
        // メッシュの描画
        bool    DrawMesh(MESH_INFO_TYPE& meshInfo, unsigned int effectIndex, unsigned int techIndex = 0) const;
        // バックバッファとフロントバッファを切り替える
        void    Present(void);

では実装しながら、みんなの不明点をつぶしていきますね。

今回のライブラリの種類はスタティック ライブラリです。このソリューションビューの画像は何もしていない最初の状態です。
プリコンパイル済みヘッダーにチェックを入れると、最初からこんな風に実装されます。
プリコンパイル済みヘッダーって何ですか?という方のために説明すると

STLとかは一回コンパイルすれば、まず変更なんてしないため、再度コンパイルする必要がありません。
ならば以降のコンパイル時はコンパイルするのをスキップしよう。

ということで、コンパイルの高速化のためにプリコンパイル済みヘッダーを使用します。

じゃあさっそく、ファイルを追加しましょう。…と、その前にファイルの置き場所をしっかり決めておかなければなりません。
スタティックライブラリを利用するには、ヘッダーファイルが必要になりますので、ヘッダーファイルだけをまとめた
Includeフォルダを作っておく必要があります。

外部が使用する機能・型などが書かれたヘッダファイルは、全てこのIncludeフォルダの中に置かなければなりません。

そのことを注意しながら実装していきます。

とりあえず、メインとなるクラスSimpleDX10を作成します。
デバイスはマシンに一つですし、アプリケーションにとってウィンドウも一つでしょうから
Singleton パターンを使ってみました。え?シングルトンって何ですかって?

Singleton のイメージは唯一のグローバル変数です。
アプリケーション実行中に、一つしかインスタンスを生成できないクラスをSingleton パターンで作られたクラスと呼びます。

使い方は簡単です。上で書いた静的な機能、Instance() を呼ぶだけで、その唯一のインスタンスを取得できます。
コンストラクタを private に隠しているので、2個以上作ろうとコードを書くとコンパイルが通りません。
とても有名なデザインパターンなので、名前と意味くらいはここで覚えておきましょう。

次にやることは、ここに示される4種類のライブラリをつくるソリューションの構成を作成することです。

ソリューションエクスプローラのソリューションを右クリックして、「プロパティ」を開くと
右上に「構成マネージャ」のボタンがあります。下図の構成マネージャを選択しても同じですが
ここで次に示すような名前で構成を新規に作成します。

UIは直感的なので問題なく追加できるはずです。あとは、それぞれの構成について、次の設定を行います。

@プロジェクトの「プロパティ」→「C++」→「コード生成」の項目を名前に合った種類に設定します。
A「C++」と同列の項目に「ビルドイベント」があると思います。「ビルド後のイベント」→「コマンドライン」
に「copy $(TargetPath) $(ProjectDir)\Dist\lib」と書き込みます。(Distフォルダとlibフォルダは自分で作ります)
B「C++」と同列の項目「ライブラリアン」→「出力」の項目にそれぞれのファイルの種類が分かるような名前を書いてください。
例:「$(OutDir)\$(ProjectName)_md_debug.lib」

あとは、構成を選択してビルドするだけで、次のように libフォルダ内にそれぞれのlibファイルが出力されます。

形だけですが、これらの libファイルと Include フォルダを提供すれば、他のプロジェクトでライブラリを使用することができます。
おや?もしかして、ファイルのアイコンに緑のチェックマークが付いていることが気になります?

緑のチェックマークですが、これは「Subversion」というファイル管理ツールを使っているからです。
ソースコードの管理を考えた時、いつ、どういった目的で、どんな変更を加えたのか確認できるとすばらしいと思いませんか?
そんな望みをかなえてくれる便利なツールがあります。それが「Subversion」です。
GUI中心で作業したいなぁと調べてみたら、次の記事を見つけました。
導入はとても簡単でした。(みんなも、ファイル管理ツールを使ってみましょう。正確に以前のバージョンを参照できるようになりますよ。)

「Subversion を Windows で GUI 中心に使う」

さて、では先ほどの機能一式を実装していきます。

ということで、実装が完了しました。(約1日使ってしまった…)

このソリューションビューのファイルで一番大きいファイルを次に示します。

SimpleDX10.cpp


さて、ライブラリが正しく機能するかどうかですが、どのようにテストすればよいでしょうか?
クラス単体で機能をテストすることを調べてみると、次の CppUnit なんてキーワードがヒットすると思います。

C++開発者のための単体テスト入門

このサイトを参考にすれば、誰でも CppUnit を実装できます。
作ったコードが、期待通りに動作するかどうかの確信を得られますので、機会があれば導入していきましょう。
さて、今回はウィンドウが出たりするので、GUIを操作してくれるテストツールとかも必要になります。
そんな時は、次のツールの使用を検討してみようと思います。

AutoIt

今はまだ、何をどうテストするか決めてないので、そこらへんが詰まってきたら手を出してみますね。

最後に、Doxygen でソースからドキュメントを作成してみましょう。

Doxygen の使い方は 22話 で詳しく扱っています。
今回の変更点は、Doxyfile の INPUT = の部分のフォルダパスを書き換えるだけです。
doxygen を実行すると、次のリファレンスドキュメントが作成されます。
(現在のリファレンスとはバージョンが異なるので、あくまで例としてとらえて下さい。2010/12/05)

SimplestarDX10_Reference

さて、ライブラリも完成したことですし
これにて今回のレッスンを終了します。
さっそく次回から、今回のライブラリを使っていきましょう。

作成した SimpleDX10 ライブラリを次のリンク先に置いておきます。
ダウンロードコーナーにも追加しました。(ちゃんとテストしてないので多分動きません…何で公開したし!)

SimplestarDX10.zip

それでは、また次回に会いましょう!

2010/11/03 初記。
2010/11/14 追加。
2010/11/20 修正、追加。

:::::::::::032話 ライブラリの作り方:::::::::::

  • BACK
  • HOME
  • NEXT
ホームページ制作 フリー素材 無料WEB素材
Copyright (C) Simplestar All Rights Reserved.