Appearance
第13回 新作プロジェクトとゲームの土台
2 / 4 DXライブラリのライフタイムとスコープ制御
このステップの目的
main.cpp におけるDXライブラリの初期化と終了処理の対応関係を正しく構築し、RAIIを用いてリソース解放の順序を安全に制御します。
画面設定とフレームレートの集約
画面解像度や目標フレームレートは、複数のクラスから参照される共通の設定値です。これらをマジックナンバーとして各所に散らさず、ApplicationConfig.h に一元管理します。
ApplicationConfig.hcpp
namespace dxgame::config
{
inline constexpr int ScreenWidth = 640;
inline constexpr int ScreenHeight = 480;
inline constexpr int FrameRate = 60;
// 1フレームあたりの経過時間(秒):約0.01667秒
inline constexpr float DeltaTime = 1.0f / FrameRate;
// 1フレームあたりの目標ミリ秒:約16.667ms
inline constexpr double FrameMilliseconds = 1000.0 / FrameRate;
}初期化と終了処理の厳密な対応
ゲームの起動エントリーポイントである main.cpp では、DXライブラリの初期化手順を決められた順序で実行します。
main.cppcpp
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
// ウィンドウモードの設定と解像度の指定
ChangeWindowMode(TRUE);
SetGraphMode(dxgame::config::ScreenWidth, dxgame::config::ScreenHeight, 32);
// DXライブラリの初期化
if (DxLib_Init() == -1)
{
// 初期化に失敗した場合は、DXライブラリが開始されていないためDxLib_Endは呼ばずに終了する
return -1;
}
// 描画先を裏画面(バックバッファ)に設定する
SetDrawScreen(DX_SCREEN_BACK);
int result = 0;
{
// 波括弧によるスコープを作る
dxgame::platformer::Game game;
if (!game.Initialize())
{
result = -1;
}
else
{
game.MainLoop();
}
} // ここで game のデストラクタが実行され、画像や音声が確実に解放される
} // ここで game のデストラクタが実行され、画像や音声が自動的に解放される
// すべてのリソースが解放された安全な状態でDXライブラリを終了する
DxLib_End();
return result;
}波括弧(スコープ)によるRAIIの保証
上記のコードで重要な点は、Game game; が意図的に波括弧 { } の内側に配置されていることです。
もし波括弧を作らずに WinMain() の直下に game を宣言してしまうと、関数の末尾で DxLib_End() が呼ばれた後に game のデストラクタが実行されることになります。
そうなると、ImageManager や SoundManager がデストラクタで DeleteGraph() や DeleteSoundMem() を呼ぼうとしたとき、すでにDXライブラリ本体が終了してしまっているため、エラーや未定義動作を引き起こします。
波括弧を設けてスコープを限定することにより、「必ず DxLib_End() が呼ばれる前に、すべてのゲーム内リソースが破棄される」 という安全な順序が言語仕様(RAII)に基づいて保証されます。
チェックリスト
-
DxLib_Init()が失敗した際に、不正なDxLib_End()を呼ばずに終了するガードがある - バックバッファ描画(
SetDrawScreen(DX_SCREEN_BACK))が設定されている -
Gameクラスが波括弧スコープ内で生成され、DxLib_Endより前に確実に破棄される理由を説明できる