マルチスレッドでロード画面を実現【DXライブラリ】
マルチスレッドでロード画面を実現【DXライブラリ】
こんにちは。
DXライブラリの力を借りて、3Dアクションゲームを作ろうとしているものです。
製作中に思わぬ壁にぶつかってしまったので、アドバイスのほどをお願いします。
3Dゲームの場合、ゲーム開始前に、まず「モデルの読み込み」といった初期化が必要になるのですが、
初期化をしているときにロード画面を表示させたいので
初期化部分の処理をマルチスレッドを使って、裏でさせようと思います。
形としては、
はじめに「裏で初期化を受け持つスレッド」を呼び出しておき、
メインのループではメッセージの処理とロード画面の描画を行い、初期化が終わるのを待つ。
そして
初期化が終わると同時に、ゲームが開始される、といった形です。
このマルチスレッドでの初期化で起こる問題なのですが、
大きいステージモデルを読み込む時に、プログラムが異常終了を起こしてしまいます。
というのも、小さいモデルで初期化を試したところ、ちゃんとゲーム開始まで実行できるのです。
また、
初期化を(裏でなく)メインの処理に組み込んだところ、大きいモデルでも読み込むことができました。
裏で行う初期化に対して
メモリの容量に制限があるのかとおもうのですが、
なにか解決の方法はありますでしょうか。
なにとぞご指導よろしくお願いします。
ちなみにスレッドの呼び出しには_beginthreadex関数を使い、きちんと呼び出し、終了はできているので
そのあたりの落ち度はないのかと思います。
DXライブラリの力を借りて、3Dアクションゲームを作ろうとしているものです。
製作中に思わぬ壁にぶつかってしまったので、アドバイスのほどをお願いします。
3Dゲームの場合、ゲーム開始前に、まず「モデルの読み込み」といった初期化が必要になるのですが、
初期化をしているときにロード画面を表示させたいので
初期化部分の処理をマルチスレッドを使って、裏でさせようと思います。
形としては、
はじめに「裏で初期化を受け持つスレッド」を呼び出しておき、
メインのループではメッセージの処理とロード画面の描画を行い、初期化が終わるのを待つ。
そして
初期化が終わると同時に、ゲームが開始される、といった形です。
このマルチスレッドでの初期化で起こる問題なのですが、
大きいステージモデルを読み込む時に、プログラムが異常終了を起こしてしまいます。
というのも、小さいモデルで初期化を試したところ、ちゃんとゲーム開始まで実行できるのです。
また、
初期化を(裏でなく)メインの処理に組み込んだところ、大きいモデルでも読み込むことができました。
裏で行う初期化に対して
メモリの容量に制限があるのかとおもうのですが、
なにか解決の方法はありますでしょうか。
なにとぞご指導よろしくお願いします。
ちなみにスレッドの呼び出しには_beginthreadex関数を使い、きちんと呼び出し、終了はできているので
そのあたりの落ち度はないのかと思います。
Re: マルチスレッドでロード画面を実現【DXライブラリ】
普通に、レースコンディション起こしてませんか?
大きいデータということですから、全部読み込みきる前にメインスレッドが走ってるような気がするのですが。
大きいデータということですから、全部読み込みきる前にメインスレッドが走ってるような気がするのですが。
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 13年前
- 住所: 東海地方
- 連絡を取る:
Re: マルチスレッドでロード画面を実現【DXライブラリ】
DXライブラリはスレッドセーフではありませんので、たぶんこちらで問題を起こしている可能性が高いです。
スプライト系は、スレッドでメモリ読み込みだけしておいて後で展開って手段が取れるのですがモデル系はなにか良い手があるかなぁ。
ちょっと、調べてみますね。
スプライト系は、スレッドでメモリ読み込みだけしておいて後で展開って手段が取れるのですがモデル系はなにか良い手があるかなぁ。
ちょっと、調べてみますね。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: マルチスレッドでロード画面を実現【DXライブラリ】
モデル系も、「スレッドでメモリ読み込みだけしておいて後で展開」は可能ですよ。softya(ソフト屋) さんが書きました: スプライト系は、スレッドでメモリ読み込みだけしておいて後で展開って手段が取れるのですがモデル系はなにか良い手があるかなぁ。
Re: マルチスレッドでロード画面を実現【DXライブラリ】
へろりさん
返信ありがとうございます。
レース・コンディションというのは初めて聞いたのですが、
初期化の途中にメインスレッドが動くのは試行錯誤の中で確認しました。
ですが、
フラグで初期化の終了を判断するようにしているので、
多分
先走っているのではないと思います。
以下のMode_Flagがそれで、初期化後に代入されるので判断しています。
以下呼び出す「初期化スレッド」↓
この場合、処理がフラグを変えるに至らずに
初期化中に異常終了が出てしまうのです。
なんとか「ステージの初期化」における「モデルの読み込み」
(DXライブラリなのでMV1LoadModel() を使いました。)が原因らしいと
突き止めましたが、モデルのサイズがある程度大きい場合に終了してしまうようなのです。
ちなみにメインスレッドで同じ初期化をしたところ大きいモデルでも異常終了しませんでした。
ここまで来たら自分でやれよ、と思われそうですが、
解決方法はありますでしょうか
ご指導お願いします。
返信ありがとうございます。
レース・コンディションというのは初めて聞いたのですが、
初期化の途中にメインスレッドが動くのは試行錯誤の中で確認しました。
ですが、
フラグで初期化の終了を判断するようにしているので、
多分
先走っているのではないと思います。
以下のMode_Flagがそれで、初期化後に代入されるので判断しています。
以下呼び出す「初期化スレッド」↓
unsigned __stdcall Load_Initialize(void *arg)
{
//3D初期化(ステージモデルが大きいと終了?)
{
Player_Initialize() ; // プレイヤーの初期化
Stage_Initialize() ; // ステージの初期化
Camera_Initialize() ; // カメラの初期化
}
Mode_Flag = 3 ; // 初期化が終わったらフラグ変更
_endthreadex( 0 );
return 0;
}
初期化中に異常終了が出てしまうのです。
なんとか「ステージの初期化」における「モデルの読み込み」
(DXライブラリなのでMV1LoadModel() を使いました。)が原因らしいと
突き止めましたが、モデルのサイズがある程度大きい場合に終了してしまうようなのです。
ちなみにメインスレッドで同じ初期化をしたところ大きいモデルでも異常終了しませんでした。
ここまで来たら自分でやれよ、と思われそうですが、
解決方法はありますでしょうか
ご指導お願いします。
最後に編集したユーザー smr_c on 2011年2月17日(木) 14:38 [ 編集 1 回目 ]
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 13年前
- 住所: 東海地方
- 連絡を取る:
Re: マルチスレッドでロード画面を実現【DXライブラリ】
h2so5さん、DXライブラリでの実現方法を書いて頂けると助かるのですが・・・。h2so5 さんが書きました:モデル系も、「スレッドでメモリ読み込みだけしておいて後で展開」は可能ですよ。softya(ソフト屋) さんが書きました: スプライト系は、スレッドでメモリ読み込みだけしておいて後で展開って手段が取れるのですがモデル系はなにか良い手があるかなぁ。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: マルチスレッドでロード画面を実現【DXライブラリ】
画像ファイルイメージの場合は、CreateGraphFromMem()を使用してメモリ上のデータを展開しますが、
3Dモデル用のMV1LoadModelFromMem() という隠し関数が存在します。
以下、DXライブラリ掲示板より引用
3Dモデル用のMV1LoadModelFromMem() という隠し関数が存在します。
以下、DXライブラリ掲示板より引用
ちなみにこの関数の要望を出したのは私です(-_-;)// メモリ上のモデルファイルイメージと独自の読み込みルーチンを使用してモデルを読み込む
int MV1LoadModelFromMem( void *FileImage, int FileSize, int (* FileReadFunc )( const char *FilePath, void **FileImageAddr, int *FileSize, void *FileReadFuncData ), int (* FileReleaseFunc )( void *MemoryAddr, void *FileReadFuncData ), void *FileReadFuncData ) ;
void *FileImage
メモリに読み込んだモデルファイルの先頭アドレス
int FileSize
メモリに読み込んだモデルファイルのサイズ
int (* FileReadFunc )( const char *FilePath, void **FileImageAddr, int *FileSize, void *FileReadFuncData )
ファイルを読み込むコールバック関数のポインタ
const char *FilePath : 読み込むべきファイルのパス( モデルファイル内に記述されているものそのまま )
int **FileImageAddr : 読み込むべきファイルのメモリアドレスを代入するポインタのアドレス
int *FileSize : 読み込むべきファイルのサイズを代入する変数のアドレス
void *FileReadFuncData : 使用者が必要な情報のアドレス( MV1LoadModelFromMem の引数 FileReadFuncData がそのまま渡されます )
コールバック関数で行う必要がある処理は
引数 FileImageAddr が示すアドレスに指定されたファイルをメモリに読み込んだもの( 若しくは既にあるもの )の先頭アドレスを代入して
引数 FileSize が示すアドレスに指定されたファイルのサイズを代入する
です
戻り値はファイルの読み込みに成功した場合は 0 を、失敗した場合は -1 を返してください
int (* FileReleaseFunc )( void *MemoryAddr, void *FileReadFuncData )
読み込んだファイルを解放するコールバック関数のポインタ
void *MemoryAddr : FileReadFunc で指定したコールバック関数内で FileImageAddr に代入したファイルイメージの先頭アドレス
void *FileReadFuncData : 使用者が必要な情報のアドレス( MV1LoadModelFromMem の引数 FileReadFuncData がそのまま渡されます )
例えば引数 FileReadFunc で指定したコールバック関数内でメモリを確保した場合、この関数で解放してください
void *FileReadFuncData
FileReadFunc と FileReleaseFunc で指定したコールバック関数に引数として渡されるアドレスです、
必要な固有データがある場合はこの引数を介して渡すことができます
関数名も説明も長くてすいません
独自のアーカイブということで、もしかしたら解放の関数も必要かもと思い、
読み込み時に呼ばれる関数と解放時に呼ばれる関数の二つのコールバック関数を渡すようになっています
この関数を使用して普通にファイルから読み込む部分をコールバック関数で処理するようにしたサンプルを
書いてみましたので、よろしければご覧になってみて下さい
#include "DxLib.h" #include <stdio.h> #include <malloc.h> // ファイルを読み込む関数 int LoadFile( const TCHAR *FilePath, void **FileImageAddr, int *FileSize ) { FILE *fp ; // ファイルを開く fp = fopen( FilePath, "rb" ) ; // 失敗したら -1 を返す if( fp == NULL ) return -1 ; // ファイルのサイズを取得 fseek( fp, 0L, SEEK_END ) ; *FileSize = ftell( fp ) ; fseek( fp, 0L, SEEK_SET ) ; // メモリの確保 *FileImageAddr = malloc( *FileSize ) ; // ファイルの読み込み fread( *FileImageAddr, *FileSize, 1, fp ) ; // ファイルを閉じる fclose( fp ) ; // 成功なら 0 を返す return 0 ; } // ファイル読み込み用コールバック関数 int FileReadFunc( const TCHAR *FilePath, void **FileImageAddr, int *FileSize, void *FileReadFuncData ) { return LoadFile( FilePath, FileImageAddr, FileSize ) ; } // ファイル解放用コールバック関数 int FileReleaseFunc( void *MemoryAddr, void *FileReadFuncData ) { // 確保したメモリの解放 free( MemoryAddr ) ; return 0 ; } // WinMain 関数 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { int ModelHandle ; void *FileImage ; int FileSize ; ChangeWindowMode( TRUE ) ; // DXライブラリの初期化 if( DxLib_Init() < 0 ) { // エラーが発生したら直ちに終了 return -1 ; } // 3Dモデルファイルの読み込み LoadFile( "DxChara.x", &FileImage, &FileSize ) ; // メモリ上に読み込んだ3Dモデルファイルから3Dモデルを作成 ModelHandle = MV1LoadModelFromMem( FileImage, FileSize, FileReadFunc, FileReleaseFunc, NULL ) ; // 画面に映る位置に3Dモデルを移動 MV1SetPosition( ModelHandle, VGet( 320.0f, -300.0f, 300.0f ) ) ; // 3Dモデルの描画 MV1DrawModel( ModelHandle ) ; // キーの入力待ち WaitKey() ; // DXライブラリの後始末 DxLib_End() ; // ソフトの終了 return 0 ; }
Re: マルチスレッドでロード画面を実現【DXライブラリ】
順番の整合性に問題無いとするとちょっと分かりませんね。
初期化が終わってフラグの変更をメインスレッドが確認した後、WaitForSingleObject() 等で、初期化スレッドの終了を待った場合はどうなりますか。
初期化が終わってフラグの変更をメインスレッドが確認した後、WaitForSingleObject() 等で、初期化スレッドの終了を待った場合はどうなりますか。
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 13年前
- 住所: 東海地方
- 連絡を取る:
Re: マルチスレッドでロード画面を実現【DXライブラリ】
h2so5さん、もう一つだけ。
このMV1LoadModelFromMem関数って、もしかして最新バージョン(3.04d)からですか?
このMV1LoadModelFromMem関数って、もしかして最新バージョン(3.04d)からですか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: マルチスレッドでロード画面を実現【DXライブラリ】
おそらく 3.03a からだと思います。softya(ソフト屋) さんが書きました:このMV1LoadModelFromMem関数って、もしかして最新バージョン(3.04d)からですか?
Re: マルチスレッドでロード画面を実現【DXライブラリ】
やってみたら確かにできたのですが、へろり さんが書きました:順番の整合性に問題無いとするとちょっと分かりませんね。
初期化が終わってフラグの変更をメインスレッドが確認した後、WaitForSingleObject() 等で、初期化スレッドの終了を待った場合はどうなりますか。
ロード画面ではアニメーションさせたいのでちょっと不向きのような気もします。(--;)
この時はサイズが大きいステージモデルでもできたので、やはりスレッドは待たないと
いけないのでしょうか。。。
ちょっとh2so5さんの隠し関数についてもやってみようと思います。
Re: マルチスレッドでロード画面を実現【DXライブラリ】
WaitForSingleObjectを待機時間0でポーリングしてもですか?smr_c さんが書きました:ロード画面ではアニメーションさせたいのでちょっと不向きのような気もします。(--;)
Re: マルチスレッドでロード画面を実現【DXライブラリ】
ISLe さんが書きました:WaitForSingleObjectを待機時間0でポーリングしてもですか?smr_c さんが書きました:ロード画面ではアニメーションさせたいのでちょっと不向きのような気もします。(--;)
完全に自分の知識不足です。申し訳ないです。
ポーリングという言葉も知りませんでした。
というわけで このようにやってみました。が
始めと同じように異常終了してしまいました。(;_;)
待機時間を1秒ほどに延ばせば大丈夫でしたが、ロード画面がかくかくしてしまいます。
GetExitCodeThreadを使っても同様です。 最後に、試しにWaitKey()を使ってロード画面を止め、少し経ってから進めたところ、
WaitForSingleObjectを使わなくても上手く進みました。
どうにもメイン処理が少し待っている必要があるみたいです。
アドバイスありがとうございました。
もう少し自分で調べてみます。
Re: マルチスレッドでロード画面を実現【DXライブラリ】
副管理人さん
h2so5さん
アドバイスありがとうございました。
ようやくおっしゃっていたことについてできました。
おっしゃっていたように
スレッドセーフではなかったことが原因だったようです。
ただ、書き込んでくださったMV1LoadModelFromMem関数を使用して
3Dモデルを作成すると、テクスチャが貼られていないまっさらなステージになってしまいました。
書き込んでくださったテストプログラムではちゃんと貼ってある状態でしたので、
これは自分で解決したいと思います。
協力してくださった方々、本当にありがとうございました。
これで、この質問は「解決」とさせていただきます。
h2so5さん
アドバイスありがとうございました。
ようやくおっしゃっていたことについてできました。
おっしゃっていたように
スレッドセーフではなかったことが原因だったようです。
ただ、書き込んでくださったMV1LoadModelFromMem関数を使用して
3Dモデルを作成すると、テクスチャが貼られていないまっさらなステージになってしまいました。
書き込んでくださったテストプログラムではちゃんと貼ってある状態でしたので、
これは自分で解決したいと思います。
協力してくださった方々、本当にありがとうございました。
これで、この質問は「解決」とさせていただきます。