トップページ > 記事閲覧
非同期読み込みで無限ロードになることがある
名前:Naotsun 日時: 2019/05/08 21:34

現在、DXライブラリとC++を用いて弾幕STGを制作しています。 タイトルにある通り、非同期読み込みについて質問です。 今回、ゲーム開始時とステージ開始時に非同期読み込みを行っていて、ゲーム開始時の非同期読み込みで無限ロードが起こってしまいます。 まず、アセットの管理方法ですが、管理専門のクラスを用いています。そのクラスのメンバ変数にstd::mapで定義し以下のメンバ関数で 読み込んでいます。 −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− ※COLOR_TYPE = 5 ※EFFECT_MAX = 30 (ヘッダー) /** フォント情報 **/ std::map<FontKey, const Font> FontInfo = { //マメロン サイズ32 - 90 {FK_Mamelon_S32, {"../Asset/Font/Mamelon_S32.dft", 32}}, {FK_Mamelon_S60, {"../Asset/Font/Mamelon_S60.dft", 60}}, {FK_Mamelon_S90, {"../Asset/Font/Mamelon_S90.dft", 90}}, }; /** フォントハンドル **/ int fontBuffer[FK_FontNum]; std::map<FontKey, int> FontHandle = { //マメロン サイズ32 - 90 {FK_Mamelon_S32, fontBuffer[0]}, {FK_Mamelon_S60, fontBuffer[1]}, {FK_Mamelon_S90, fontBuffer[2]}, }; /** グラフィック情報 **/ std::map<GraphicKey, const Graphic> GraphicInfo = { //プレイヤー { GK_Player , { "../Asset/Graphics/Player/Player.png", 5, 1, 5, 70, 70 }}, //方向弾を撃つ敵 { GK_DirectionalShoter , { "../Asset/Graphics/Enemy/EnemyTriangle.png", 5, 1, 5, 63, 70 }}, //渦巻弾を撃つ敵 { GK_SpiralShoter , { "../Asset/Graphics/Enemy/EnemyBox.png", 5, 1, 5, 70, 70 }}, //特殊弾を撃つ敵 { GK_SpecialShoter , { "../Asset/Graphics/Enemy/EnemyHexagon.png", 5, 1, 5, 70, 70 }}, //破壊エフェクト { GK_BreakEffect , { "../Asset/Graphics/Effect/BreakEffect.png", 30 * 5, 5 * 5, 6, 400, 400 }}, //カラーチェンジエフェクト { GK_ColorChangeEffect, { "../Asset/Graphics/Effect/ColorChangeEffect.png", 30 * 5, 5 * 5, 6, 400, 400 }}, //捕食エフェクト { GK_PredationEffect, { "../Asset/Graphics/Effect/PredationEffect.png", 30 * 5, 5 * 5, 6, 400, 400 }}, //リスポーンエフェクト { GK_RespawnEffect, { "../Asset/Graphics/Effect/RespawnEffect.png", 30 * 5, 5 * 5, 6, 400, 400 }}, //ブロックエフェクト { GK_BlockEffect, { "../Asset/Graphics/Effect/BlockEffect.png", 30 * 5, 5 * 5, 6, 400, 400 }}, //レベルアップエフェクト { GK_LevelUpEffect, { "../Asset/Graphics/Effect/LevelUpEffect.png", 30 * 5, 5 * 5, 6, 400, 400 }}, //ボス登場エフェクト { GK_BossAppearanceEffect, { "../Asset/Graphics/Effect/BossAppearance.png", 30, 5, 6, 800, 800 }}, //プレイヤーの通常弾 { GK_PlayerNormalBullet, { "../Asset/Graphics/Bullet/PlayerNormal.png", 5, 1, 5, 62, 40 }}, //プレイヤーの円形弾 { GK_PlayerCircleBullet, { "../Asset/Graphics/Bullet/PlayerCircle.png", 5, 1, 5, 40, 40 }}, //敵の方向弾 { GK_EnemyDirectionalBullet, { "../Asset/Graphics/Bullet/EnemyDirectional.png", 5, 1, 5, 62, 40 }}, //敵の渦巻弾 { GK_EnemySpiralBullet, { "../Asset/Graphics/Bullet/EnemySpiral.png", 5, 1, 5, 40, 40 }}, //敵の追尾弾 { GK_EnemyAimingBullet, { "../Asset/Graphics/Bullet/EnemyAiming.png", 5, 1, 5, 80, 40 }}, }; /** グラフィックハンドル **/ std::map<GraphicKey, int*> GraphicHandle = { //プレイヤー { GK_Player, new int[COLOR_TYPE] }, //方向弾を撃つ敵 { GK_DirectionalShoter, new int[COLOR_TYPE]}, //渦巻弾を撃つ敵 { GK_SpiralShoter, new int[COLOR_TYPE]}, //特殊弾を撃つ敵 { GK_SpecialShoter, new int[COLOR_TYPE]}, //破壊エフェクト { GK_BreakEffect, new int[EFFECT_MAX * COLOR_TYPE]}, //カラーチェンジエフェクト { GK_ColorChangeEffect, new int[EFFECT_MAX * COLOR_TYPE]}, //捕食エフェクト { GK_PredationEffect, new int[EFFECT_MAX * COLOR_TYPE]}, //リスポーンエフェクト { GK_RespawnEffect, new int[EFFECT_MAX * COLOR_TYPE]}, //ブロックエフェクト { GK_BlockEffect, new int[EFFECT_MAX * COLOR_TYPE]}, //レベルアップエフェクト { GK_LevelUpEffect, new int[EFFECT_MAX * COLOR_TYPE]}, //ボス登場エフェクト { GK_BossAppearanceEffect, new int[EFFECT_MAX]}, //プレイヤーの通常弾 { GK_PlayerNormalBullet, new int[COLOR_TYPE]}, //プレイヤーの円形弾 { GK_PlayerCircleBullet, new int[COLOR_TYPE]}, //敵の方向弾 { GK_EnemyDirectionalBullet, new int[COLOR_TYPE]}, //敵の渦巻弾 { GK_EnemySpiralBullet, new int[COLOR_TYPE]}, //敵の追尾弾 { GK_EnemyAimingBullet, new int[COLOR_TYPE]}, }; /** オーディオ情報 **/ std::map<AudioKey, const Audio> AudioInfo = { //決定SE {AK_Decison, {"../Asset/Audio/SE/Decision.mp3", ST_SE, 2}}, //カーソル移動SE {AK_Cursor, {"../Asset/Audio/SE/Cursor.mp3", ST_SE, 2}}, //上書きSE {AK_Overwrite, {"../Asset/Audio/SE/Overwrite.mp3", ST_SE, 2}}, //プレイヤーの射撃SE {AK_PlayerShot, {"../Asset/Audio/SE/PlayerShot.mp3", ST_SE, 100}}, //敵の射撃SE 1 {AK_EnemyShot1, {"../Asset/Audio/SE/Shot1.mp3", ST_SE, 1000}}, //敵の射撃SE 2 {AK_EnemyShot2, {"../Asset/Audio/SE/Shot2.mp3", ST_SE, 1500}}, //敵の射撃SE 3 {AK_EnemyShot3, {"../Asset/Audio/SE/Shot3.mp3", ST_SE, 1000}}, //ボス登場SE {AK_BossAppearance, {"../Asset/Audio/SE/BossAppearance.mp3", ST_SE, 1}}, //ヒットSE {AK_Hit, {"../Asset/Audio/SE/Hit.mp3", ST_SE, 1000}}, //レベルアップSE {AK_LevelUp, {"../Asset/Audio/SE/LevelUp.mp3", ST_SE, 1}}, //破壊SE {AK_Break, {"../Asset/Audio/SE/Break.mp3", ST_SE, 50}}, //カラーチェンジSE {AK_ColorChange, {"../Asset/Audio/SE/ColorChange.mp3", ST_SE, 3}}, //捕食SE {AK_Predation, {"../Asset/Audio/SE/Predation.mp3", ST_SE, 10}}, //ブロックSE {AK_Block, {"../Asset/Audio/SE/Block.mp3", ST_SE, 1000}}, //タイトルBGM {AK_Title, {"../Asset/Audio/BGM/ギリシャの夜明け.mp3", ST_BGM, 1}}, //設定BGM {AK_Setting, {"../Asset/Audio/BGM/忘れられた神殿.mp3", ST_BGM, 1}}, //クレジットBGM {AK_Credit, {"../Asset/Audio/BGM/カナリアスキップ.mp3", ST_BGM, 1}}, }; /** オーディオハンドル **/ int audioBuffer[AK_AudioNum]; std::map<AudioKey, int> AudioHandle = { //決定SE {AK_Decison, audioBuffer[0]}, //カーソル移動SE {AK_Cursor, audioBuffer[1]}, //上書きSE {AK_Overwrite, audioBuffer[2]}, //プレイヤーの射撃SE {AK_PlayerShot, audioBuffer[3]}, //敵の射撃SE 1 {AK_EnemyShot1, audioBuffer[4]}, //敵の射撃SE 2 {AK_EnemyShot2, audioBuffer[5]}, //敵の射撃SE 3 {AK_EnemyShot3, audioBuffer[6]}, //ボス登場SE {AK_BossAppearance, audioBuffer[7]}, //ヒットSE {AK_Hit, audioBuffer[8]}, //レベルアップSE {AK_LevelUp, audioBuffer[9]}, //破壊SE {AK_Break, audioBuffer[10]}, //カラーチェンジSE {AK_ColorChange, audioBuffer[11]}, //捕食SE {AK_Predation, audioBuffer[12]}, //ブロックSE {AK_Block, audioBuffer[13]}, //タイトルBGM {AK_Title, audioBuffer[14]}, //設定BGM {AK_Setting, audioBuffer[15]}, //クレジットBGM {AK_Credit, audioBuffer[16]}, }; (実装ファイル) //全てのグラフィックアセットを読み込む void CAssetManager::LoadAllGraphicAssets() { //グラフィックハンドルのロード for (auto itr = GraphicHandle.begin(); itr != GraphicHandle.end(); itr++) { //読み込むアセットがなければ異常終了 if (LoadDivGraphic(GraphicInfo[itr->first], itr->second) == -1) { //エラーメッセージを作成 std::string file_path = GraphicInfo[itr->first].FileName; std::string err_msg = "AssetManager : ファイルパス [" + file_path + "] の画像が読み込めませんでした。"; //エラーメッセージをポップアップ Process.PutErrWindow(err_msg.c_str()); } } } //全てのオーディオアセットを読み込む void CAssetManager::LoadAllAudioAssets() { //オーディオハンドルのロード for (auto itr = AudioHandle.begin(); itr != AudioHandle.end(); itr++) { itr->second = LoadSoundMem(AudioInfo[itr->first].FileName, AudioInfo[itr->first].Buffer); //読み込むアセットがなければ異常終了 if (itr->second == -1) { //エラーメッセージを作成 std::string file_path = AudioInfo[itr->first].FileName; std::string err_msg = "AssetManager : ファイルパス [" + file_path + "] の音声が読み込めませんでした。"; //エラーメッセージをポップアップ Process.PutErrWindow(err_msg.c_str()); } } } −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 上記の関数をSetUseASyncLoadFlagで非同期読み込みをオンにしてから実行し、同じくメンバ関数の非同期読み込みが完了したかを調べる以下の 関数でロードの終了を確認しています。 −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− (実装ファイル) //全てのアセットの非同期読み込みが完了しているか bool CAssetManager::AreAllAssetsLoaded(int& loadingNum) { int num = 0; bool isLoaded = true; //グラフィックハンドルの確認 for (auto itr = GraphicHandle.begin(); itr != GraphicHandle.end(); itr++) { //いずれかの読み込みが終わってなければfalseを返す bool isAllLoaded = true; for (int i = 0; i < GraphicInfo[itr->first].Count; i++) { if (CheckHandleASyncLoad(itr->second[i])) isAllLoaded = false; } if (!isAllLoaded) { isLoaded = false; num++; } } //オーディオハンドルの確認 for (auto itr = AudioHandle.begin(); itr != AudioHandle.end(); itr++) { //読み込みが終わってなければfalseを返す if (CheckHandleASyncLoad(itr->second)) { isLoaded = false; num++; } } //ロード中のアセットの数を返す loadingNum = num; //全てのアセットの非同期読み込みが終了していたらtrueを返す return isLoaded; } −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 上記の関数でブレークポイントを置いたりして確認したところ、必ずグラフィックハンドルのエフェクトのいずれかが読み込まれず無限ロードに なっていました。ですので、エフェクトとそれ以外のグラフィックで分割する数が違うのでそれが原因かとも思いましたが、非同期読み込みでない 場合には正常に読み込むため原因がわからず質問させていただきました。 ご教授の程よろしくお願いいたします。
メンテ

Page: 1 |

Re: 非同期読み込みで無限ロードになることがある ( No.1 )
名前:管理人 日時:2019/05/10 00:59

手元で LoadDivGraph を SetUseASyncLoad( TRUE ); で非同期読み込みをするテストを 行ってみましたが、正常に動作しました Naotsunさんが現象に遭遇された状況のような、複数の読み込み処理の組み合わせで 発生する不具合かもしれません… 手元で現象を確認できれば原因はある程度容易にわかると思いますので もし不都合が無ければ現象の確認に必要なファイルやプログラムを zip 等で圧縮して こちらのメールアドレス BQE00322(あっとまーく)nifty.com ( (あっとまーく)を@に置き換えてください ) に送っていただけないでしょうか? m(_ _;m
メンテ
Re: 非同期読み込みで無限ロードになることがある ( No.2 )
名前:Naotsun 日時:2019/05/10 21:31

ご対応ありがとうございます。 セーブデータの暗号化に別のライブラリを使用していたため、 その部分のみコメントアウトしたVisual Studio 2017のプロジェクトごと 指定のメールアドレスまでお送りいたしました。 リファクタリングが途中ですのでわかりにくいかと思いますが、ご了承ください。 何卒、ご教授の程よろしくお願いいたします。
メンテ
Re: 非同期読み込みで無限ロードになることがある ( No.3 )
名前:管理人 日時:2019/05/11 02:44

メールありがとうございます、プログラムを拝見しました 早速実行してみましたが、読み込みが終わらないという状況にはなりませんでした 送っていただいたプログラムは SetUseASyncLoadFlag( TRUE ); を無効にして 正常に読み込みが完了するようになっているものでしょうか…? ただ、ゲームを開始するまでに CObjectList::New の assert(size <= mObjectSize); に 何度もオブジェクトのサイズがリストの最大サイズを超えて引っ掛かっていたのが気になりました もしかしたらメモリリークが発生しているかもしれません ( PLAYER_SIZE と ENEMY_SIZE と EFFECT_SIZE と PLAYER_BULLET_SIZE と UT_PARTS_SIZE の  値を大きくしたら assert に引っ掛からなくなり、ゲーム画面まで進めました ) よろしければご確認ください m(_ _)m
メンテ
Re: 非同期読み込みで無限ロードになることがある ( No.4 )
名前:Naotsun 日時:2019/05/11 07:59

お送りしたプログラムは私の環境では無限ロードになる状態のものです。 また、オブジェクトリストのサイズですがこちらは私の環境では問題ありませんでした。 オブジェクトリストのサイズについては私の環境での最小値にしていたので、 環境が違い確保するサイズも変わったためではないかと思います。 (Main.cpp)      //フォントデータをロード AssetManager.LoadFontData(); //非同期読み込みを行うように SetUseASyncLoadFlag(TRUE); //全てのグラフィックアセットをロード AssetManager.LoadAllGraphicAssets(); //全てのオーディオアセットをロード AssetManager.LoadAllAudioAssets(); の部分で私の場合これだと無限ロードになりグラフィックアセットのロードを SetUseASyncLoadFlagの前に実行すると無限ロードにはなりませんでした。 オーディオアセットのロードは非同期読み込みでも問題なく動作します。 また、お手数をおかけして大変恐縮ではありますが、オブジェクトリストのサイズを それぞれどのくらいの大きさにしたのかを教えて頂けないでしょうか?
メンテ
Re: 非同期読み込みで無限ロードになることがある ( No.5 )
名前:Naotsun 日時:2019/05/11 20:09

同じプログラムで結果が違うので気になり色々と試したところ、私は32bitマシン(x86)の環境でデバッグしていました。 そこで64bitマシン(x64)の環境でデバッグしたところ、問題なくロードすることができました。またオブジェクトリストの最大サイズも おっしゃる通り大きくなっていました。 丁寧なご対応をして頂きありがとうございました。 また、私の確認不足で大手数をお掛けして申し訳ありませんでした。
メンテ
Re: 非同期読み込みで無限ロードになることがある ( No.6 )
名前:管理人 日時:2019/05/12 02:38

私も x86環境で Main.cpp の SetOutApplicationLogValidFlag(FALSE); をコメントアウトして 実行してみたところ、メモリ不足でロードが失敗していました ロード時にはメモリが余計に消費されるので、非同期読み込みで大量のロードが 同時に行われたことで使用できるメモリに制限がある x86環境ではメモリが足りなくなっていた ( 非同期読み込み OFF の場合は一つづつ読み込まれるため、空きメモリ不足にならなかった ) ということのようです 問題が解決したようで何よりです m(_ _)m
メンテ
Re: 非同期読み込みで無限ロードになることがある ( No.7 )
名前:Naotsun(解決済み) 日時:2019/05/12 10:14

なるほど、そういうことだったんですね。 今回の件で色々と勉強になりました。 重ね重ねにはなりますが、ご丁寧な対応ありがとうございました。 解決済みとさせていただきます。
メンテ

Page: 1 |

題名
名前
コメント
パスワード (記事メンテ時に使用)

   クッキー保存