マルチスレッドでロード画面を実現【DXライブラリ】

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
smr_c
記事: 15
登録日時: 9年前
住所: 埼玉県
連絡を取る:

マルチスレッドでロード画面を実現【DXライブラリ】

#1

投稿記事 by smr_c » 9年前

こんにちは。
DXライブラリの力を借りて、3Dアクションゲームを作ろうとしているものです。

製作中に思わぬ壁にぶつかってしまったので、アドバイスのほどをお願いします。


3Dゲームの場合、ゲーム開始前に、まず「モデルの読み込み」といった初期化が必要になるのですが、
初期化をしているときにロード画面を表示させたいので
初期化部分の処理をマルチスレッドを使って、裏でさせようと思います。

形としては、
はじめに「裏で初期化を受け持つスレッド」を呼び出しておき、
メインのループではメッセージの処理とロード画面の描画を行い、初期化が終わるのを待つ。
そして
初期化が終わると同時に、ゲームが開始される、といった形です。

このマルチスレッドでの初期化で起こる問題なのですが、
大きいステージモデルを読み込む時に、プログラムが異常終了を起こしてしまいます。

というのも、小さいモデルで初期化を試したところ、ちゃんとゲーム開始まで実行できるのです。

また、
初期化を(裏でなく)メインの処理に組み込んだところ、大きいモデルでも読み込むことができました。

裏で行う初期化に対して
メモリの容量に制限があるのかとおもうのですが、
なにか解決の方法はありますでしょうか。

なにとぞご指導よろしくお願いします。


ちなみにスレッドの呼び出しには_beginthreadex関数を使い、きちんと呼び出し、終了はできているので
そのあたりの落ち度はないのかと思います。

アバター
へろりくしょん
記事: 92
登録日時: 10年前
住所: 福岡

Re: マルチスレッドでロード画面を実現【DXライブラリ】

#2

投稿記事 by へろりくしょん » 9年前

普通に、レースコンディション起こしてませんか?

大きいデータということですから、全部読み込みきる前にメインスレッドが走ってるような気がするのですが。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 10年前
住所: 東海地方
連絡を取る:

Re: マルチスレッドでロード画面を実現【DXライブラリ】

#3

投稿記事 by softya(ソフト屋) » 9年前

DXライブラリはスレッドセーフではありませんので、たぶんこちらで問題を起こしている可能性が高いです。
スプライト系は、スレッドでメモリ読み込みだけしておいて後で展開って手段が取れるのですがモデル系はなにか良い手があるかなぁ。
ちょっと、調べてみますね。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
h2so5
副管理人
記事: 2212
登録日時: 9年前
住所: 東京
連絡を取る:

Re: マルチスレッドでロード画面を実現【DXライブラリ】

#4

投稿記事 by h2so5 » 9年前

softya(ソフト屋) さんが書きました: スプライト系は、スレッドでメモリ読み込みだけしておいて後で展開って手段が取れるのですがモデル系はなにか良い手があるかなぁ。
モデル系も、「スレッドでメモリ読み込みだけしておいて後で展開」は可能ですよ。

smr_c
記事: 15
登録日時: 9年前
住所: 埼玉県
連絡を取る:

Re: マルチスレッドでロード画面を実現【DXライブラリ】

#5

投稿記事 by smr_c » 9年前

へろりさん
返信ありがとうございます。

レース・コンディションというのは初めて聞いたのですが、
初期化の途中にメインスレッドが動くのは試行錯誤の中で確認しました。

ですが、
フラグで初期化の終了を判断するようにしているので、
多分
先走っているのではないと思います。


以下の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
登録日時: 10年前
住所: 東海地方
連絡を取る:

Re: マルチスレッドでロード画面を実現【DXライブラリ】

#6

投稿記事 by softya(ソフト屋) » 9年前

h2so5 さんが書きました:
softya(ソフト屋) さんが書きました: スプライト系は、スレッドでメモリ読み込みだけしておいて後で展開って手段が取れるのですがモデル系はなにか良い手があるかなぁ。
モデル系も、「スレッドでメモリ読み込みだけしておいて後で展開」は可能ですよ。
h2so5さん、DXライブラリでの実現方法を書いて頂けると助かるのですが・・・。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
h2so5
副管理人
記事: 2212
登録日時: 9年前
住所: 東京
連絡を取る:

Re: マルチスレッドでロード画面を実現【DXライブラリ】

#7

投稿記事 by h2so5 » 9年前

画像ファイルイメージの場合は、CreateGraphFromMem()を使用してメモリ上のデータを展開しますが、
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 ;
}
ちなみにこの関数の要望を出したのは私です(-_-;)

アバター
へろりくしょん
記事: 92
登録日時: 10年前
住所: 福岡

Re: マルチスレッドでロード画面を実現【DXライブラリ】

#8

投稿記事 by へろりくしょん » 9年前

順番の整合性に問題無いとするとちょっと分かりませんね。

初期化が終わってフラグの変更をメインスレッドが確認した後、WaitForSingleObject() 等で、初期化スレッドの終了を待った場合はどうなりますか。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 10年前
住所: 東海地方
連絡を取る:

Re: マルチスレッドでロード画面を実現【DXライブラリ】

#9

投稿記事 by softya(ソフト屋) » 9年前

h2so5さん、もう一つだけ。
このMV1LoadModelFromMem関数って、もしかして最新バージョン(3.04d)からですか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
h2so5
副管理人
記事: 2212
登録日時: 9年前
住所: 東京
連絡を取る:

Re: マルチスレッドでロード画面を実現【DXライブラリ】

#10

投稿記事 by h2so5 » 9年前

softya(ソフト屋) さんが書きました:このMV1LoadModelFromMem関数って、もしかして最新バージョン(3.04d)からですか?
おそらく 3.03a からだと思います。

smr_c
記事: 15
登録日時: 9年前
住所: 埼玉県
連絡を取る:

Re: マルチスレッドでロード画面を実現【DXライブラリ】

#11

投稿記事 by smr_c » 9年前

へろり さんが書きました:順番の整合性に問題無いとするとちょっと分かりませんね。

初期化が終わってフラグの変更をメインスレッドが確認した後、WaitForSingleObject() 等で、初期化スレッドの終了を待った場合はどうなりますか。
やってみたら確かにできたのですが、
ロード画面ではアニメーションさせたいのでちょっと不向きのような気もします。(--;)

この時はサイズが大きいステージモデルでもできたので、やはりスレッドは待たないと
いけないのでしょうか。。。

ちょっとh2so5さんの隠し関数についてもやってみようと思います。

ISLe
記事: 2648
登録日時: 10年前
連絡を取る:

Re: マルチスレッドでロード画面を実現【DXライブラリ】

#12

投稿記事 by ISLe » 9年前

smr_c さんが書きました:ロード画面ではアニメーションさせたいのでちょっと不向きのような気もします。(--;)
WaitForSingleObjectを待機時間0でポーリングしてもですか?

smr_c
記事: 15
登録日時: 9年前
住所: 埼玉県
連絡を取る:

Re: マルチスレッドでロード画面を実現【DXライブラリ】

#13

投稿記事 by smr_c » 9年前

ISLe さんが書きました:
smr_c さんが書きました:ロード画面ではアニメーションさせたいのでちょっと不向きのような気もします。(--;)
WaitForSingleObjectを待機時間0でポーリングしてもですか?

完全に自分の知識不足です。申し訳ないです。
ポーリングという言葉も知りませんでした。

というわけで

コード:

WaitForSingleObject( thread_id, 0);
このようにやってみました。が
始めと同じように異常終了してしまいました。(;_;)

待機時間を1秒ほどに延ばせば大丈夫でしたが、ロード画面がかくかくしてしまいます。
GetExitCodeThreadを使っても同様です。

コード:

GetExitCodeThread( thread_id, &dwExitCode);
最後に、試しにWaitKey()を使ってロード画面を止め、少し経ってから進めたところ、
WaitForSingleObjectを使わなくても上手く進みました。

どうにもメイン処理が少し待っている必要があるみたいです。
アドバイスありがとうございました。
もう少し自分で調べてみます。

smr_c
記事: 15
登録日時: 9年前
住所: 埼玉県
連絡を取る:

Re: マルチスレッドでロード画面を実現【DXライブラリ】

#14

投稿記事 by smr_c » 9年前

副管理人さん
h2so5さん

アドバイスありがとうございました。
ようやくおっしゃっていたことについてできました。
おっしゃっていたように
スレッドセーフではなかったことが原因だったようです。

ただ、書き込んでくださったMV1LoadModelFromMem関数を使用して
3Dモデルを作成すると、テクスチャが貼られていないまっさらなステージになってしまいました。

書き込んでくださったテストプログラムではちゃんと貼ってある状態でしたので、
これは自分で解決したいと思います。

協力してくださった方々、本当にありがとうございました。
これで、この質問は「解決」とさせていただきます。

閉鎖

“C言語何でも質問掲示板” へ戻る