ページ 11

C++/DxLibで大量の画像を管理する方法

Posted: 2013年9月29日(日) 17:09
by site_sw
はじめまして。C++/DXライブラリを使用してゲームを作成しています。
作成中に画像ハンドルの管理方法に疑問を感じたので質問します。
今回はあらゆる画像情報(ハンドルなど)をvectorに格納しているのですが、表示の際に
目的の画像を見つけ出すために毎回vectorの先頭から検索する必要が出てきてしまっています。

ゲーム中には、いろいろな種類の画像が出てきます。(背景、キャラクター、メッセージ枠など)
そこで、画像を管理するために以下のようなクラスを作りました。
基底クラスGraphicおよびこれを継承した背景・キャラクタークラスを作成しました。
また、画像情報をvector型で持つ画像管理クラス(GraphicManager)を作成しました。(例のため簡略化しています)

コード:

//画像の基底クラス
class Graphic{
public:
    //画像の種類:背景、キャラクター、その他
    enum GraphicType{
        type_background,
        type_character,
        type_other
    }
    Graphic();
    int handle;//グラフィックハンドル
    int graphic_type;//画像の種類(enum GraphicTypeのどれかが入る)
};

//背景画像クラス
class BackGroundGraphic : public Graphic{
public:
    //コンストラクタ内で 引数typeを元にgraphic_type、idを元にbackground_idを設定します。
    //またDxLib::LoadGraph(filename)を行い、グラフィックハンドルhandleを設定します。
    BackGroundGraphic(int id, int type, char *filename);
    //背景画像固有の変数
    int background_id;
};

//キャラクター画像クラス
class CharacterGraphic : public Graphic{
public:
    //コンストラクタ内で 引数typeを元にgraphic_type、idを元にcharacter_idを設定します。
    //またDxLib::LoadGraph(filename)を行い、グラフィックハンドルhandleを設定します。
    CharacterGraphic(int id, int type, char *filename);
    //キャラクター画像固有の変数
    int character_id;
    bool visible;
};

//画像管理クラス
class GraphicManager{
public:
    GraphicManager();
    std::vector<Graphic> vec;
}
画像を読み込む際には、以下のように使用します。

コード:

GraphicManager g_manager;
//vecにキャラクター画像を挿入
g_manager.vec.push_back( CharacterGraphic( 100, type_character, "image1.png" ) );
g_manager.vec.push_back( CharacterGraphic( 101, type_character, "image2.png" ) );
//vecに背景画像を挿入
g_manager.vec.push_back( BackGroundGraphic( 200, type_background, "back.png" ) );
このように実装すると、画像を全て一つのvectorで管理できるので便利ですが、表示の際が面倒です。
例えばvectorに画像が100枚格納されているとき、id=100のキャラクター画像とid=200の背景画像を表示する、とします。
その際、vector内をループで回して
graphic_type == type_character && character_id == 100 のもの、および
graphic_type == type_background && character_id == 200 のものを毎フレームごとに検索する必要があります。
この例では2種類の画像を検索するので、最大200回ループすることになります。

PCの処理は早いとはいえ、このように何百回もループするような処理が積み重なり処理が重くなる可能性があるのでは、と考えています。
一般的には、どのようにして大量の画像を管理をし、表示の際にはどのように目的の画像を探しだすものなのでしょうか。
これに比べ良い方法があるのか、それともvectorだから検索は早いので、探し出す時間は無視しても良いのでしょうか。

Re: C++/DxLibで大量の画像を管理する方法

Posted: 2013年9月29日(日) 17:20
by softya(ソフト屋)
vectorのindexを各クラスが保持したら処理が高速で簡単になるのでは無いでしょうか?
初回だけは検索が必要になると思いますが。

あと、id別にvectorを持った方が楽になると思います。

それと
g_manager.vec.push_back()
はオブジェクト指向的にはカプセル化が出来ていないので良くないです。

Re: C++/DxLibで大量の画像を管理する方法

Posted: 2013年9月29日(日) 17:51
by site_sw
softya(ソフト屋) さんが書きました:vectorのindexを各クラスが保持したら処理が高速で簡単になるのでは無いでしょうか?
初回だけは検索が必要になると思いますが。

あと、id別にvectorを持った方が楽になると思います。

それと
g_manager.vec.push_back()
はオブジェクト指向的にはカプセル化が出来ていないので良くないです。
アドバイスありがとうございます。
いくつか疑問点があるので、質問させていただきたく思います。

vectorのindexを各クラスが保持する、というのは、例えばキャラクター画像はvectorの何番目と何番目にあるのか
という情報をクラス毎にint配列等で持たせる、という意味なのでしょうか。

id別にvectorを持たせる、というのは、キャラクター画像のみのvector、背景画像のみのvectorを作成するべき、という認識で合っていますか。
(GraphicManagerを画像の種類共通としてではなく、キャラクター版および背景版のGraphicManagerをそれぞれ定義
するべき、ということですよね)

g_manager.vec.push_back() の記法がオブジェクト指向的にまずい、とのことですが、どういった解決策があるのでしょうか。
(オブジェクト指向に慣れていないため、幼稚な質問ですがご容赦下さればと思います)
キャラクターや背景など、異なるクラスのインスタンスを全て無理矢理Graphic型にしているのが問題なのでしょうか。

全てのアドバイスに対して質問を返してしまうようで申し訳ありません。
宜しくお願いいたします。

Re: C++/DxLibで大量の画像を管理する方法

Posted: 2013年9月29日(日) 18:11
by softya(ソフト屋)
site_sw さんが書きました: vectorのindexを各クラスが保持する、というのは、例えばキャラクター画像はvectorの何番目と何番目にあるのか
という情報をクラス毎にint配列等で持たせる、という意味なのでしょうか。
配列かどうかはクラスの構造が不明なので分かりませんが、intで持たせるということです。
site_sw さんが書きました: id別にvectorを持たせる、というのは、キャラクター画像のみのvector、背景画像のみのvectorを作成するべき、という認識で合っていますか。
(GraphicManagerを画像の種類共通としてではなく、キャラクター版および背景版のGraphicManagerをそれぞれ定義
するべき、ということですよね)
idとcharactor_idの関係がよく分かりませんので勘違いしていたらすいません。
どのぐらい数のグループがあるのか、どういう単位で読み込まれ解放されるのか、あたりを考慮して決めなければ行けない事だと思います。
ここで出てきている情報だと細かい所が分からないので、大雑把な回答になるのは申し訳ないです。
site_sw さんが書きました: g_manager.vec.push_back() の記法がオブジェクト指向的にまずい、とのことですが、どういった解決策があるのでしょうか。
(オブジェクト指向に慣れていないため、幼稚な質問ですがご容赦下さればと思います)
キャラクターや背景など、異なるクラスのインスタンスを全て無理矢理Graphic型にしているのが問題なのでしょうか。
g_manager.vecがpublicな所です。
publicな変数は0個が望ましいです。

どうやってアクセスするかというと、クラスで演算子をオーバーロードするとか、アクセス用のメンバ関数を用意するとか場合によって解決策は違います。
まぁ、vectorとかstringとかのクラスのオーバーロードやメンバ関数の使い方が参考になるんじゃないでしょうか。

【補足】
クラス化したのは便利に管理するためだと思いますので、管理クラスにロード、解放、画像ハンドルの管理を全て委ねるべきだと思います。
あと管理している番号とか管理クラスが全て生成すべきで、100とか指定しているのは管理が面倒になるかミスする原因となります。
管理クラスとのインターフェイスは、ロード時の引数にグラフィックの種類とファイルのパス、戻り値がint型とかクラスのポインタでもなんでも良いので、画像を表示する時に使えるもの。
表示用のメンバ関数は、先ほどの戻り値を引数にして画像ハンドルが返却されるんで良いんじゃないでしょうか。
解放は、ロード時の戻り値とか、ファイルのパスとか管理の都合で決めればよいでしょう。
※ 一例に過ぎませんので、作るもの次第で大幅に変えたほうが良いです。

Re: C++/DxLibで大量の画像を管理する方法

Posted: 2013年9月29日(日) 18:21
by h2so5
std::map か std::unordered_map を使ってはどうでしょうか。

Re: C++/DxLibで大量の画像を管理する方法

Posted: 2013年9月30日(月) 10:36
by usao
なんかIDというのとハンドルというのが役割かぶってるような…?
(DXライブラリ知らないのでダメかもしれないけど)こんな感じでどうでしょう?
画像のロードが必要な場合は常に GraphicManager::LoadAndRegisterImg() で行い,
その戻り値を,描画する側で覚えておく.

コード:

typedef int HGRAPHIC;  //※DXライブラリの「画像ハンドル」はintなのかな?

//画像管理クラス
class GraphicManager{
public:
    GraphicManager(){}
    ~GraphicManager(){  ReleaseAllImg();  }
private:  //複製の禁止(多分必要なのかな)
    GraphicManager( const GraphicManager & );
    GraphicManager &operator=( const GraphicManager & );
public:
    //FilePathStrで指定されたファイルパスの画像をロードし,
    //そのハンドルをm_GraphVecに追加する.
    //また,戻り値としてそのハンドル値を返す.
    HGRAPHIC LoadAndRegisterImg( const char *FilePathStr );
private:
    //画像群の破棄
    void ReleaseAllImg()
    {
        m_GraphVecに登録されているハンドルの画像を全て捨てる;
        m_GraphVec.clear();
    }
private:
    std::vector< HGRAPHIC > m_GraphVec;  //画像ハンドルのvector
};