現在音ゲーを製作中なのですが

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
空道

現在音ゲーを製作中なのですが

#1

投稿記事 by 空道 » 13年前

はじめまして、空道と申します。
早速ですが質問させていただきます。

現在音ゲーを作成中なのですが、曲の一覧を取得するのにてこずっています。
形式としては、StepManiaやこちらで配布されているAerobeatPlusのようにファイルを作ることで自動的にデータを読み取るものを目指しています。
今のところ、FindFirstFile系列で特定のフォルダ以下に入っているフォルダを列挙し、その中にtxtファイルがあれば開く。
txtファイルには曲情報(曲名や、BPM)などが入っており、それぞれを用意した構造体にいれていく事で一覧表示できるようにしています。
しかし、ファイル列挙→txtファイルならば特定の処理を行う、まではうまく行ったのですが、その先のファイルオープンから中身を構造体に入れることができません。

そこで質問なのですが、
一、charで宣言したファイルパスではfopenで開けないのでしょうか。
二、もし開けるのであれば、中身はfgetsで一行ごとに読み込むのがいいのでしょうか。それとも配列に一文字ずつ入れるほうがいいのでしょうか
三、この方法でいいのでしょうか。もしも他に効率の良い方法があればヒントだけでもいいので教えていただけると幸いです。

また、列挙からデータ取得まで一発で行っています。

コード:

//構造体宣言
typedef struct {
	char *filepath;
	char *title, *rubi;
	int bpm, music_long;
	int difficulty_easy, difficulty_nomale, difficulty_hard, difficulty_caos;
} NewMusicInfo;
NewMusicInfo MusicInfo[400];

//ファイル列挙
void music_folder (char *pszBasePath) {
	char szSearchPath[MAX_PATH+1];
	char szSubPath[MAX_PATH+1];
	char szFileName[MAX_PATH+1];
	int n = 0;
	WIN32_FIND_DATA fd;

	lstrcpy(szSearchPath, pszBasePath);
	if (szSearchPath[lstrlen(szSearchPath)-1] != '*') {
		lstrcat(szSearchPath, "*");
	}

	HANDLE hFind = FindFirstFile(szSearchPath, &fd);

	do {      
		if (lstrcmp(fd.cFileName, "..") != 0 && lstrcmpi(fd.cFileName, ".") != 0) {
			if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {            
				// ディレクトリの場合
				lstrcpy(szSubPath, pszBasePath);
				lstrcat(szSubPath, fd.cFileName);
				lstrcat(szSubPath, "\\");
				music_folder(szSubPath);

			} else {
				// ファイルの場合
				lstrcpy(szFileName, pszBasePath);
				lstrcat(szFileName, fd.cFileName);
				if (stricmp(PathFindExtension(szFileName),TEXT(".txt")) == 0) { //txtファイルであれば
					FILE *fp;
					char fileline[256];
					fp = fopen(szFileName,"r");
					if (fp == NULL) { printDx( "File Open Error" ); break; }
					int linenomber = 0, n = 0;
					while (NULL != (fgets(fileline, 256, fp))) {
						//構造体にデータを入れる処理
					}
				}
			}
		}
	} while(FindNextFile(hFind, &fd));
	FindClose(hFind);
	return;
}

//WinMainにてmusic_folder("data/Movie/");として関数を呼び出しています。
日本語のおかしい所があるかもしれません。
もし、意味が伝わらないところがあれば遠慮なく言ってくださって結構です。
また、ファイル列挙のアルゴリズムに関しては検索時に出てきたものを使用しています。
長文、乱文失礼しました。

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

Re: 現在音ゲーを製作中なのですが

#2

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

全コードがないので、こちらで動作確認できないのですが、ファイルパスがちゃんと加工できているかMesssageBoxやデバッガで確認されましたでしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
空道
記事: 5
登録日時: 13年前

Re: 現在音ゲーを製作中なのですが

#3

投稿記事 by 空道 » 13年前

softya(ソフト屋) さんが書きました:全コードがないので、こちらで動作確認できないのですが、ファイルパスがちゃんと加工できているかMesssageBoxやデバッガで確認されましたでしょうか?
返信ありがとうございます。
一応、ファイル処理のエラーが起きない、ファイル名単体であれば表示されることから加工できていると勝手に思い込んでいます。
cおよびc++言語に関しては疎いので本当に出来ているかと問われると断言できないのですが……。
また、全コードとありますが関係の無い部分を含めて提示したほうが良いのでしょうか。
全コードとなると、かなり読みにくいものを晒してしまう事になりますがよろしいでしょうか。

また、ファイルパスの確認については

コード:

               if (stricmp(PathFindExtension(szFileName),TEXT(".txt")) == 0) { //txtファイルであれば
                    FILE *fp;
                    char fileline[256];
                    fp = fopen(szFileName,"r");
                    if (fp == NULL) { printDx( "File Open Error" ); break; }
                    int linenomber = 0, n = 0;
                    while (NULL != (fgets(fileline, 256, fp))) {
                        //構造体にデータを入れる処理
                    }
                }
上の部分を

コード:

               if (stricmp(PathFindExtension(szFileName),TEXT(".txt")) == 0) { //txtファイルであれば
                    printDx (szFileName);
                }
のようにして確認しました。

アバター
ゆーずぃ
記事: 62
登録日時: 13年前
住所: 埼玉県

Re: 現在音ゲーを製作中なのですが

#4

投稿記事 by ゆーずぃ » 13年前

>ファイル処理のエラーが起きない
"File Open Error"にはならないということですか?

ゲスト

Re: 現在音ゲーを製作中なのですが

#5

投稿記事 by ゲスト » 13年前

ゆーずぃ さんが書きました:>ファイル処理のエラーが起きない
"File Open Error"にはならないということですか?
はい、そういうことです。

アバター
空道
記事: 5
登録日時: 13年前

Re: 現在音ゲーを製作中なのですが

#6

投稿記事 by 空道 » 13年前

ゲスト さんが書きました:
ゆーずぃ さんが書きました:>ファイル処理のエラーが起きない
"File Open Error"にはならないということですか?
はい、そういうことです。
申し訳ありません。
ログインが切れていたようです。
上の記事は私のものです。

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

Re: 現在音ゲーを製作中なのですが

#7

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

何にしても、そこの部分だけを取り出して動くプログラムを作ってちゃんとテストしてみてください。
分からなければ、そのプログラムだけ提示してもらえば良いです。
本人の思い込みってのが一番バグの原因だったりします(私自身の失敗の経験からしても)。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
ゆーずぃ
記事: 62
登録日時: 13年前
住所: 埼玉県

Re: 現在音ゲーを製作中なのですが

#8

投稿記事 by ゆーずぃ » 13年前

試しに
while (NULL != (fgets(fileline, 256, fp)))を
while (fgets(fileline, 256, fp) != NULL)に変えてみて下さい。
結合規則の問題で関数の実行よりも先にNULLとの比較が行われるパターンがあります。
上手くいかないときは逆にしたり判定を別にしたりすると上手くいったりします。

アバター
bitter_fox
記事: 607
登録日時: 13年前
住所: 大阪府

Re: 現在音ゲーを製作中なのですが

#9

投稿記事 by bitter_fox » 13年前

>>二、もし開けるのであれば、中身はfgetsで一行ごとに読み込むのがいいのでしょうか。それとも配列に一文字ずつ入れるほうがいいのでしょうか
今回の場合は、fgetsで一行ごと読む込むので良いと思います。
fscanfという関数があるにはあるのですが、根本的にこの関数はぶっ飛んだ仕様ですので、実用的ではないです。

>>三、この方法でいいのでしょうか。もしも他に効率の良い方法があればヒントだけでもいいので教えていただけると幸いです。
もし一行の文字数が255を超えていたら、一行のすべてのデータを読み込めないので動的に確保するべきです。

>>ファイルオープンから中身を構造体に入れることができません。
strtokで分割して構造体の各要素に代入するのが定石だと考えます。

アバター
空道
記事: 5
登録日時: 13年前

Re: 現在音ゲーを製作中なのですが

#10

投稿記事 by 空道 » 13年前

先ほど他の処理を消してデバックしたところ、きちんと動作してくれました。
原因が何なのかは分からなかったですが、ゆっくりと解析していきたいと思います。
大変お騒がせして申し訳ありませんでした。
また、さまざまな書き込みありがとうございました。

アバター
空道
記事: 5
登録日時: 13年前

Re: 現在音ゲーを製作中なのですが

#11

投稿記事 by 空道 » 13年前

解決したようなことを言ったのですが、また行き詰ったのでご教授お願いします。

↓data.txt

コード:

サンプル
さんぷる
sample
↓main.cpp

コード:

//変数宣言
int now_music_no, max_music_no;

//構造体宣言
typedef struct {
	char *filepath;
	char *title, *rubi;
	int bpm, music_long;
	int difficulty_easy, difficulty_nomale, difficulty_hard, difficulty_caos;
} NewMusicInfo;
NewMusicInfo MusicInfo[400];

//キー入力情報
int GetHitKeyStateAll_2(int GetHitKeyStateAll_InputKey[]) {
	char GetHitKeyStateAll_Key[256];
	GetHitKeyStateAll( GetHitKeyStateAll_Key );
	for(int i=0;i<256;i++){
		if(GetHitKeyStateAll_Key[i]==1) GetHitKeyStateAll_InputKey[i]++;
		else                            GetHitKeyStateAll_InputKey[i]=0;
	}
	return 0;
}

//フォルダ列挙
void music_folder (char *pszBasePath) {
	char szSearchPath[MAX_PATH+1];
	char szSubPath[MAX_PATH+1];
	char szFileName[MAX_PATH+1];
	int n = 0;
	WIN32_FIND_DATA fd;

	lstrcpy(szSearchPath, pszBasePath);
	if (szSearchPath[lstrlen(szSearchPath)-1] != '*') {
		lstrcat(szSearchPath, "*");
	}

	HANDLE hFind = FindFirstFile(szSearchPath, &fd);

	do {      
		if (lstrcmp(fd.cFileName, "..") != 0 && lstrcmpi(fd.cFileName, ".") != 0) {
			if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {            
				// ディレクトリの場合
				lstrcpy(szSubPath, pszBasePath);
				lstrcat(szSubPath, fd.cFileName);
				lstrcat(szSubPath, "\\");
				music_folder(szSubPath);

			} else {
				// ファイルの場合
				lstrcpy(szFileName, pszBasePath);
				lstrcat(szFileName, fd.cFileName);
				if (stricmp(PathFindExtension(szFileName),TEXT(".txt")) == 0) {
					FILE *fp;
					char fileline[256];
					fp = fopen(szFileName,"r");
					if (fp == NULL) { MusicInfo[0].title = "FileOpenError"; break; } //ファイル確認のためMusicInfo[0].titleにいれています。
					int linenomber = 0;
					while ((fgets(fileline, 256, fp)) != NULL) {
						MusicInfo[n].title = fileline;
						printfDx(fileline); //ファイル確認のためいれています。
						n++;
					}
				max_music_no = n - 1;
				}
			}
		}
	} while(FindNextFile(hFind, &fd));
	FindClose(hFind);
	return;
}

//結果表示
void desk_graphic() {
	if (Key[ KEY_INPUT_UP ] == 1) {
		now_music_no -= 1;
		if (now_music_no < 0) now_music_no = max_music_no;
	} else if (Key[ KEY_INPUT_DOWN ] == 1) {
		now_music_no += 1;
		if (now_music_no > max_music_no) now_music_no = 0;
	}
	DrawString(100,100, "filelist" , GetColor( 255 , 255 , 255 ));
	DrawString(200,100, MusicInfo[now_music_no].title, GetColor( 255 , 255 , 255 ));
	DrawString(200,130, MusicInfo[1].title, GetColor( 255 , 255 , 255 )); //確認用
	DrawString(200,70, MusicInfo[2].title, GetColor( 255 , 255 , 255 )); //確認用
}

//メイン処理
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
	ChangeWindowMode(TRUE);//ウィンドウモード
	if(DxLib_Init() == -1 || SetDrawScreen( DX_SCREEN_BACK )!=0) return -1;//初期化と裏画面化

	PlayMovieToGraph( MovieGraphHandle ) ;
	while(ProcessMessage()==0 && ClearDrawScreen()==0 && GetHitKeyStateAll_2(Key)==0 && Key[KEY_INPUT_ESCAPE]==0){
		music_folder("data/Movie/");
		desk_graphic();
		ScreenFlip();
	}
 
	DxLib_End();
	return 0;
}
現在このようなコードにしています。
しかしながら、こうするとMusicInfo[n].titleの中身がすべて同一のものになっています。(現在の例で行くとMusicInfo[n].titleがすべてsampleに)
私としては、MusicInfo[0].titleに"サンプル"が、MusicInfo[1].titleに"さんぷる"、MusicInfo[2].titleに"sample"が入ってほしいのですが……。

そこで質問なのですが、
一、このコードの何がまずいのか。(ヒントだけでも結構です)
二、下記のコードにすると構造体の中身が空になる理由。
を教えていただけると幸いです。

コード:

//一番上に追加
int mode_select = 0;

//メイン処理
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
	ChangeWindowMode(TRUE);//ウィンドウモード
	if(DxLib_Init() == -1 || SetDrawScreen( DX_SCREEN_BACK )!=0) return -1;//初期化と裏画面化

	PlayMovieToGraph( MovieGraphHandle ) ;
	while(ProcessMessage()==0 && ClearDrawScreen()==0 && GetHitKeyStateAll_2(Key)==0 && Key[KEY_INPUT_ESCAPE]==0){
		switch(mode_select) {
		case 0:
			music_folder("data/Movie/");
			mode_select = 30;
			break;
		case 30:
			desk_graphic();
			break;
		}
		ScreenFlip();
	}
 
	DxLib_End();
	return 0;
}

アバター
bitter_fox
記事: 607
登録日時: 13年前
住所: 大阪府

Re: 現在音ゲーを製作中なのですが

#12

投稿記事 by bitter_fox » 13年前

空道 さんが書きました:

コード:

 
    char fileline[256];
    fp = fopen(szFileName,"r");
    if (fp == NULL) { MusicInfo[0].title = "FileOpenError"; break; } //ファイル確認のためMusicInfo[0].titleにいれています。
    int linenomber = 0;
    while ((fgets(fileline, 256, fp)) != NULL) { 
        MusicInfo[n].title = fileline;
        printfDx(fileline); //ファイル確認のためいれています。
        n++; 
    }
一、このコードの何がまずいのか。(ヒントだけでも結構です)
fileline のアドレスを仮に 0x008000とすると。
まず、fgets関数で0x008000には、「サンプル」が代入されます。
MusicInfo[0].title = fileline はMusicInfo[0]のtitleというポインタに filelineのアドレスを代入するという意味なので、MusicInfo[0].title の値は 0x008000になり、その参照先の値は、「サンプル」になります。

次に、fgets関数で0x008000に、「さんぷる」が代入され、
MusicInfo[1]のtitle というポインタにfilelineのアドレスを代入して値が 0x008000になり、その参照先の値は、「さんぷる」なり、MusicInfo[0]のtitleの値も0x008000なので、参照先の値は、「さんぷる」になります。

最後に、もう一度fgets関数で0x008000に、「sample」が代入され、
MusicInfo[2]のtitleというポインタにfilelineのアドレスを代入して値が0x008000になり、その参照先の値は、「sample」になり、MusicInfo[0]及びMusicInfo[1]のtitleの値も0x008000で、参照先の値も同様に「sample」になってしまいます。

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

Re: 現在音ゲーを製作中なのですが

#13

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

組み合わせてもコンパイルが通らないです。
解析するのも大変のなので、動くプログラムを提示してもらえいでしょうか?

あとデバッガでトレースしましたか?
これをするだけで大半のバグは取れます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
空道
記事: 5
登録日時: 13年前

Re: 現在音ゲーを製作中なのですが

#14

投稿記事 by 空道 » 13年前


fileline のアドレスを仮に 0x008000とすると。
まず、fgets関数で0x008000には、「サンプル」が代入されます。
MusicInfo[0].title = fileline はMusicInfo[0]のtitleというポインタに filelineのアドレスを代入するという意味なので、MusicInfo[0].title の値は 0x008000になり、その参照先の値は、「サンプル」になります。

次に、fgets関数で0x008000に、「さんぷる」が代入され、
MusicInfo[1]のtitle というポインタにfilelineのアドレスを代入して値が 0x008000になり、その参照先の値は、「さんぷる」なり、MusicInfo[0]のtitleの値も0x008000なので、参照先の値は、「さんぷる」になります。

最後に、もう一度fgets関数で0x008000に、「sample」が代入され、
MusicInfo[2]のtitleというポインタにfilelineのアドレスを代入して値が0x008000になり、その参照先の値は、「sample」になり、MusicInfo[0]及びMusicInfo[1]のtitleの値も0x008000で、参照先の値も同様に「sample」になってしまいます。
何とか別々に表示することが出来ました。
ご教授ありがとうございます。

閉鎖

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