Skip to content

第08回 画像とリソース管理

前回: 第07回 クラス分割入門 / 次回: 第09回 ゲーム状態

今回の授業内容

  • DXライブラリで画像を読み込む
  • 画像ハンドルをImageManagerで管理する
  • プレイヤー、弾、敵の描画を画像へ置き換える
  • 画像がない場合は図形描画へ戻す
  • Assets/Imagesの配置ルールを確認する

今回の開始状態

第07回終了時点では、プレイヤー、弾、敵がクラスに分かれています。
描画はまだ図形だけです。

今回の終了時点では、画像がある場合は画像で描画し、画像がない場合は今までの図形で動くようにします。

変更するファイル

ファイル変更内容
ImageManager.h / ImageManager.cpp画像の読み込み、取得、解放を追加する
Game.h / Game.cppImageManagerを所有し、初期化と終了処理で呼ぶ
Player.cpp / Bullet.cpp / Enemy.cpp描画時に画像管理を参照する
Assets/Images任意で画像素材を配置する

ImageManagerを追加する

画像の種類は列挙型で表します。

cpp
enum class ImageId
{
    Player,
    Enemy,
    Bullet,
    Background,
    Count
};

ImageManagerは、画像ハンドルを配列で持ちます。
読み込みに失敗した画像は-1のまま扱います。

cpp
class ImageManager
{
public:
    bool Initialize();
    void Finalize();
    int Get(ImageId id) const;
    bool Has(ImageId id) const;

private:
    int handles[static_cast<int>(ImageId::Count)] = {};
};

Initialize()では、ファイルパスを1か所に集めます。

cpp
handles[ToIndex(ImageId::Player)] = LoadGraph(L"Assets/Images/Player.png");
handles[ToIndex(ImageId::Enemy)] = LoadGraph(L"Assets/Images/Enemy.png");
handles[ToIndex(ImageId::Bullet)] = LoadGraph(L"Assets/Images/Bullet.png");
handles[ToIndex(ImageId::Background)] = LoadGraph(L"Assets/Images/Background.png");

描画側を差し替える

各クラスのDraw()は、ImageManagerを参照で受け取る形へ変更します。

cpp
void Player::Draw(const ImageManager& images) const
{
    if (images.Has(ImageId::Player))
    {
        DrawGraph(
            static_cast<int>(position.x - 16),
            static_cast<int>(position.y - 16),
            images.Get(ImageId::Player),
            TRUE
        );
        return;
    }

    DrawTriangle(
        static_cast<int>(position.x),
        static_cast<int>(position.y - 18),
        static_cast<int>(position.x - 16),
        static_cast<int>(position.y + 14),
        static_cast<int>(position.x + 16),
        static_cast<int>(position.y + 14),
        cyan,
        TRUE
    );
}

画像がある場合は画像を描き、ない場合はこれまでの図形描画へ戻します。
この分岐により、素材がそろっていない状態でもゲームロジックの確認を続けられます。

Gameから画像管理を渡す

GameImageManagerを所有し、描画時に各クラスへ渡します。

cpp
bool Game::Initialize()
{
    images.Initialize();
    player.Reset();
    return true;
}

void Game::Finalize()
{
    images.Finalize();
}
cpp
player.Draw(images);

for (const Bullet& bullet : bullets)
    bullet.Draw(images);

動作確認

  1. 画像なしで実行し、図形描画のまま動くことを確認する
  2. Assets/Images/Player.pngを追加し、プレイヤーだけ画像になることを確認する
  3. ファイル名を一時的に変えて、図形描画へ戻ることを確認する
  4. ImageManager::Finalize()で読み込んだ画像を解放していることを確認する

よくある問題

  • 画像が表示されない場合は、実行時の作業ディレクトリからAssets/Imagesが見えるか確認する
  • パスの大文字小文字が、ファイル名と一致しているか確認する
  • Draw()の引数を変えたら、呼び出し側もすべて修正する

今回の完成条件

  • ImageManagerで画像をまとめて管理できる
  • 画像がある場合だけ画像描画へ切り替えられる
  • 画像がない場合でもゲームを実行できる
  • Assets/Imagesの配置ルールを説明できる

まとめ

今回は、画像素材を任意で使える構成にしました。
次回は、タイトル、プレイ中、結果画面をゲーム状態として整理します。