さて、サンプルプログラムであればmain.cppに詰め込んで実行するパターンが多かったと思いますが、
中〜大規模な設計においてはmain.cppになんでも詰め込むわけにはいきません。
そこで、まずソースコードの骨格を作ります。
main.cppにはほとんど書くことがありません。
main.cpp
#include <DxLib.h> #include "SystemMain.h" int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){ SystemMain system; if (system.initialize()) { system.main(); } system.finalize(); }
DXライブラリに関するあれこれ必要な処理をしてくれるクラスをSystemMainと名付けました。
DXライブラリをある程度使ったことがある方なら分かる通り、処理はwhile文内をぐるぐるまわり、その中で必要な処理を制御していきます。
このぐるぐるまわす部分をよく「looper」と呼びます。
DXライブラリの面倒な処理は全部SystemMainがやり、グルグル回る部分をLooper.loop()がやるように設計してみます。
SystemMain.h
#pragma once class SystemMain final { public: SystemMain() = default; ~SystemMain() = default; bool initialize() const; void finalize() const; void main() const; };
SystemMain.cpp
#include <DxLib.h> #include "SystemMain.h" #include "Define.h" #include "Looper.h" /*! * @brief DXライブラリやゲームの初期処理 */ bool SystemMain::initialize() const { SetAlwaysRunFlag(TRUE); //ウィンドウがノンアクティブでも実行 SetWindowSizeChangeEnableFlag(TRUE); //ウィンドウサイズを自由に変更できるようにする SetOutApplicationLogValidFlag(FALSE); //ログ出力しない SetFullScreenResolutionMode(DX_FSRESOLUTIONMODE_DESKTOP); //フルスクリーン時に縦横比を維持する SetWindowText("四聖龍神録2"); //ウィンドウタイトルを付ける ChangeWindowMode(TRUE); //ウィンドウモードに変更 // SetWindowSizeExtendRate(1.0); //ウィンドウサイズを変更したい時はここに倍率を指定する const int COLOR_BIT = 32; //色のbit数。通常32で良いが軽くするなら16にする SetGraphMode(Define::WIN_W, Define::WIN_H, COLOR_BIT); //ゲーム画面の解像度を設定する if (DxLib_Init()) { //DXライブラリ初期化処理 return false; //異常終了したら即座にやめる } SetDrawScreen(DX_SCREEN_BACK); //裏画面処理を設定する return true; } /*! * @brief DXライブラリやゲームの終了処理 */ void SystemMain::finalize() const { DxLib_End(); } /*! * @brief メイン処理 */ void SystemMain::main() const { Looper looper; while (!ScreenFlip() && !ProcessMessage() && !ClearDrawScreen()) { if (!looper.loop()) { break; } } }
Looper.h
#pragma once class Looper final { public: Looper(); ~Looper(); bool loop() const; };
Looper.cpp
#include "Looper.h" Looper::Looper() { } Looper::~Looper() { } bool Looper::loop() const { return true; }
Define.h
#pragma once class Define final { public: const static int WIN_W; //ウィンドウサイズ横 const static int WIN_H; //ウィンドウサイズ縦 const static float PI; //円周率 };
Define.cpp
#include "Define.h" const int Define::WIN_W = 1280; //ウィンドウサイズ横 const int Define::WIN_H = 960; //ウィンドウサイズ縦 const float Define::PI = 3.141592654f; //円周率
SystemMainではDXライブラリ関連の処理をしています。
initializeでは見慣れない関数がいくつかあるかもしれませんが、特にスルーして構いません。
DXライブラリの細かい関数の使い方は必要になった時にリファレンスを見ればよいです。
SystemMainはinitialize処理が終わった後は、ひたすらLooper.loop()を呼び出しているだけです。
loopが偽を返して来たらループを抜けてゲームを終了します。
ゲームに必要な処理はこのLooper.loop()内に書いていくことになります。
また、ゲーム内で共通して参照したい定義もあります。
例えばウィンドウサイズ等は色んな所から参照したい定義です。
ここでDefineクラスを定義しました。
ここに必要な定義を追加していきます。
ポイントは.hの中には宣言のみにし、定義は.cppの方に書くことです。
.hは変更するとインクルードしている全てのファイルがリコンパイル対象になります。
ファイルがまだ数個のうちはよいですが、クラスが何百個にもなった時、定義値を一つ変えるだけでコンパイルに数分も待たなくてはならないようになってしまいます。
このような場合変数の宣言のみを.hに書いておき、定義を.cppにすることで、.cppを変更してもリコンパイルされないので大幅にコンパイルが効率的になります。
また、定義を利用する時に
#define PI 3.141592f
のような記述をすることがありますが、これはやってはいけません。
const staticな定義をするべきです。何故かはEffective C++を参照してください。
更に、DirectXの環境を始めとするDXライブラリのような環境ではdouble型には意味がありません。
DirectXが勝手にFPUコントロールレジスタを変更してしまうためdoubleで計算してもfloatの精度に落ちてしまうのです。
従ってDXライブラリのプロジェクトでdoubleを使う必要性はありません。
計算精度に差は無いにも関わらず使用するメモリサイズが倍になっていいことが無いので全てfloatを利用して計算します。
(※最近のDXライブラリは利用可能な場合はdouble型を有効にするのでDXライブラリの関数に渡さない限りは意味のある場合があるようですが本書では考えません)
今回の実行結果は画面が2章よりちょっと大きくなった程度であまり変わりありません。
最初にやらなければならないことが色々あり、見た目変化がなく面白くありませんが、しばしお付き合いください。
次の章でLooperの中身を作り込んでいきましょう。