ページ 11

for文の繰り返し回数

Posted: 2011年10月22日(土) 00:34
by ふぅ
毎回お世話になっております。

一定時間表示された数字を記憶し、それをこたえていくゲームを作っています。
画像の表示時間を調整しようとfor文を用いたのですが、60回くりかえすように記述したはずが、一度しか処理されません。(画像が一瞬だけ表示されて消えます。)
何が原因なのでしょうか?
「seikai」と「matigai」の関数の部分です。
開発環境はDXライブラリ Microsoft Visual C++ 2010です。

ビルドログ
1>------ ビルド開始: プロジェクト: GameProg, 構成: Debug Win32 ------
1> test.cpp
1>c:\users\hajime\desktop\gameprog\test.cpp(135): warning C4789: メモリ コピーのターゲットが小さすぎます
1>c:\users\hajime\desktop\gameprog\test.cpp(136): warning C4789: メモリ コピーのターゲットが小さすぎます
1> GameProg.vcxproj -> C:\Users\hajime\Desktop\GameProg\Debug\GameProg.exe
========== ビルド: 1 正常終了、0 失敗、0 更新不要、0 スキップ ==========

コード:

#include "DxLib.h"
#include "キーボードモジュール.h"

int ketteion;//キー操作音格納変数
int sentakuon[1];//キー操作音格納変数

int i;
int Ready;//レベルごとの開始待機用
int G_Lv[6];//レベル表示の画像
int anntenn;//暗転用画像
int taitoru;//タイトル画面の画像
int a[5] = {250,300,350,400,450};//矢印の座標格納
int houkouKey;//矢印の画像
int gameover;//ゲームオーバーの画像格納変数
int HP;//ライフゲージの画像格納変数(未使用)

int d;//選択番号
int mode;/*現在のゲームモード*/


int furagu[30];//判定用変数
int Lv1[3];//レベルごとの問題の乱数を格納
int LvA;//キーボードからの数値を格納し、成否を判定する
int stage;//ステージの判定変数
int LIFE=3;//残りのHP

int TRUE_A;//正解の画像格納変数
int NO_A;//不正解の画像格納変数

//正解した時の画像処理
void seikai(){int c;
				c=0;
				for(c=0;c <= 60;c++){//一秒間表示
					DrawGraph(0,0,TRUE_A,TRUE);//正解の画像の表示
							   }
			 }
void matigai(){int c;
				c=0;
				for(c=0;c <= 60;c++){//一秒間表示
				DrawGraph(0,0,NO_A,TRUE);//不正解の画像の表示
				                  }
				
			  }

void taitoru_syokika(){
	Ready=LoadGraph("画像/レディ?.png");
	taitoru = LoadGraph("画像/無題.png");
	houkouKey = LoadGraph("画像/方向キー右.png");
	sentakuon[0]=LoadSoundMem("効果音/キー移動音.ogg");
	ketteion=LoadSoundMem("効果音/決定音.ogg");
	anntenn=LoadGraph("画像/暗転.png");
	G_Lv[0]=LoadGraph("画像/レベル0.png");
	G_Lv[1]=LoadGraph("画像/レベル1.png");
	G_Lv[2]=LoadGraph("画像/レベル2.png");
	G_Lv[3]=LoadGraph("画像/レベル3.png");
	G_Lv[4]=LoadGraph("画像/レベル4.png");
	G_Lv[5]=LoadGraph("画像/レベル5.png");
	TRUE_A=LoadGraph("画像/正解.png");
	NO_A=LoadGraph("画像/失敗.png");
	gameover=LoadGraph("画像/ゲームオーバー.png");
	HP=LoadGraph("画像/ライフ.png");

	furagu[2]=1;//最初のレベルを1に設定
	}
void taitoru_enzan(){				if(Keyboard_Get(KEY_INPUT_DOWN) == 1){ d = (d + 1 ) % 5  ;/*下が押されたら選択番号を下げる*/
																		  PlaySoundMem(sentakuon[0],DX_PLAYTYPE_BACK,TRUE);}
									if(Keyboard_Get(KEY_INPUT_UP)== 1 ){ d = (d + 4 ) %  5;//上が押されたら選択番号を上げる
																		  PlaySoundMem(sentakuon[0],DX_PLAYTYPE_BACK,TRUE);}
									
									if(Keyboard_Get(KEY_INPUT_RETURN)==1 && d==0){PlaySoundMem(ketteion,DX_PLAYTYPE_BACK,TRUE);
																					mode=1;
																				}
									if(Keyboard_Get(KEY_INPUT_RETURN)==1 && d==1){mode=26;}
									if(Keyboard_Get(KEY_INPUT_RETURN)==1 && d==3){mode=27;}
									

											}
void taitoru_byouga(){
						DrawGraph( 0, 0, taitoru,TRUE);
						DrawFormatString( 400,250,GetColor(255,255,255),"ゲームスタート");
						DrawFormatString( 400,300,GetColor(255,255,255),"記録回覧");//未実装
						DrawFormatString( 400,350,GetColor(255,255,255),"ヘルプ");//未実装
						DrawFormatString( 400,400,GetColor(255,255,255),"設定");//未実装
						DrawFormatString( 400,450,GetColor(255,255,255), "ゲーム終了");
						
						DrawGraph(383,a[d],houkouKey,TRUE);//方向キーを選択番号に対応
} //画像の描画
void GameMain_Mondai1(){

	static int count;
	count++;
	if(count==1){Lv1[0] = GetRand(9999);}//4桁の数をランダムで表示
	if(Lv1[0]<=1000){Lv1[0] = 1000+Lv1[0];}//数が3ケタだった場合4桁に修正する。
	if(count<=60){DrawFormatString( 300,230,GetColor(255,255,255),"%d",Lv1[0]);}//一秒たつまで問題を表示
	if(count>=60){count=0;
					mode=4;}}//問題を表示し終わったらカウンターを初期化し、回答処理へ

void GameMain_answer1(){DrawFormatString( 230,180,GetColor(255,255,255),"4桁の数字を入力してください");
		LvA = KeyInputNumber( 230 , 200 , 9999 , 0 , FALSE );//9999の数まで数値を受け付ける
		if(LvA == Lv1[0]){seikai();//正解した場合、画像処理を行い
							stage=stage+1;//正解数を加算
							
							}
		else{				matigai();//不正解だった場合、不正解の画像処理を行い
							LIFE= LIFE-1;}//ライフゲージを1減らす。
		
		if(stage==3){mode=1;}//三回正解したらレベル振り分けのクラスへ
				else{if(LIFE==0){mode=25;}//ライフゲージが0になったらゲームオーバの処理へ
						else{mode=3;}}//その他の場合もう一度このレベルで出題
}
void GameMain_Mondai2(){

	static int count;
	count++;
	if(count==1){Lv1[2] = GetRand(9999);}//4桁の数をランダムで表示
	if(Lv1[2]<=1000){Lv1[2] = 1000+Lv1[2];}//数が3ケタだった場合4桁に修正する。
	if(count<=40){DrawFormatString( 300,230,GetColor(255,255,255),"%d",Lv1[2]);}
	if(count>=40){count=0;
	mode=7;}}
void GameMain_answer2(){DrawFormatString( 230,180,GetColor(255,255,255),"4桁の数字を入力してください");
	
		LvA = KeyInputNumber( 230 , 200 , 9999 , 0 , FALSE );
		
		if(LvA == Lv1[2]){DrawGraph(0,0,TRUE_A,TRUE);
							stage=stage+1;}
		else{DrawGraph(0,0,NO_A,TRUE);
		LIFE= LIFE-1;}
			if(stage==3){mode=1;}
			else{if(LIFE==0){mode=25;}
			else{mode=6;}}}
void GameMain_Mondai3(){

	static int count;
	count++;
	if(count==1){Lv1[3] = GetRand(89999);}//4桁の数をランダムで表示
	if(Lv1[3]<=10000){Lv1[3] = 1000+Lv1[3];}//数が3ケタだった場合4桁に修正する。
	if(count<=60){DrawFormatString( 300,230,GetColor(255,255,255),"%d",Lv1[3]);}
	if(count>=60){count=0;
	mode=10;}}

void GameMain_answer3(){DrawFormatString( 230,180,GetColor(255,255,255),"5桁の数字を入力してください");
	
		LvA = KeyInputNumber( 230 , 200 , 99999 , 0 , FALSE );
		
		if(LvA == Lv1[3]){DrawGraph(0,0,TRUE_A,TRUE);
							stage=stage+1;}
		else{DrawGraph(0,0,NO_A,TRUE);
		LIFE= LIFE-1;}
			if(stage==3){mode=1;}
			else{if(LIFE==0){mode=25;}
			else{mode=9;}}}
int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
        ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定

			
			
taitoru_syokika();//画像処理
		
        // while( 裏画面を表画面に反映, メッセージ処理, 画面クリア )
        while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 ){

               	 Keyboard_Update();//キーボードモジュールの読み込み

				switch(mode){			
							case 0:	taitoru_enzan();//タイトル画面の演算								
				   					if(Keyboard_Get(KEY_INPUT_RETURN)==1 && d==4){
										PlaySoundMem(ketteion,DX_PLAYTYPE_BACK,TRUE);
										DxLib_End(); // DXライブラリ終了処理
										return 0;break;}
												
									taitoru_byouga();//タイトル画面の表示
									ScreenFlip();//画像を反転
									
									break;

							case 1:i++;//各レベルへの振り分け。
								   DrawGraph(0,0,G_Lv[furagu[2]],TRUE);//レベルに対応した画像を表示
								   if(i==60){i=0;//一秒表示したらカウンターを初期化
											 furagu[2]++;//レベルアップ
											 stage=0;//正解数初期化
								   if(furagu[2]==2){mode=2;}//レベル1へ
								   if(furagu[2]==3){mode=5;}//レベル2へ
								   if(furagu[2]==4){mode=8;}//レベル3へ
								   if(furagu[2]==5){mode=11;}}//レベル4へ(未実装)
								   break;



						   //以下レベル1
							case 2:	DrawGraph(0,0,Ready,TRUE);
									if(Keyboard_Get(KEY_INPUT_SPACE)==1){
										mode=3;}
															  break;

									case 3:GameMain_Mondai1();break;
									case 4:GameMain_answer1();break;
										
	
	
	
							//以下レベル2
								case 5:	DrawGraph(0,0,Ready,TRUE);
										if(Keyboard_Get(KEY_INPUT_SPACE)==1){
										mode=6;}
										break;
								case 6:GameMain_Mondai2();break;
								case 7:GameMain_answer2();break;



							//以下レベル3
								case 8:DrawGraph(0,0,Ready,TRUE);
										if(Keyboard_Get(KEY_INPUT_SPACE)==1){
											mode=9;}
										break;
								case 9 :GameMain_Mondai3();break;
								case 10:GameMain_answer3();break;
								
								//ゲームオーバーの処理
								case 25:DrawGraph(0,0,gameover,TRUE);
									if(Keyboard_Get(KEY_INPUT_SPACE)==1){furagu[2]=0;mode=0;}
									break;
								case 26:break;}
				
			
				
				
		}
			
		
        
		DxLib_End(); // DXライブラリ終了処理
        return 0;
}  

Re: for文の繰り返し回数

Posted: 2011年10月22日(土) 01:03
by Dixq (管理人)
ソースコード少し拝見しましたが、あまりにもコードが整理されなさすぎです。
どこがどこの括弧に対応しているか全く分からないです。
まずはきちんとインデントし、見やすいコードを書くことから始めて下さい。

質問の件ですが、
for文で60回繰り返しても、1フレームに60回同じ場所へ描画されるだけです。
ゲームのメインループは1回まわって1フレームとなります、すなわち、同じ場所で回ってもだめです。
この辺の基本的なことはゲームプログラミングの館に書いてあるのでご覧ください。
http://dixq.net/g/

いきなり大きなプログラムを書かず、3章やd章を読んで、まずはゲームの動かしかたや、設計の仕方を勉強してみて下さい。

Re: for文の繰り返し回数

Posted: 2011年10月22日(土) 01:06
by 沖 滉均
念のためもう1点
for(c=0;c <= 60;c++){//一秒間表示
上記は60回まわそうとしているなら間違っています。
0~60まで61回ループです

Re: for文の繰り返し回数

Posted: 2011年10月22日(土) 01:14
by ふぅ
素早い返信ありがとうございました。

ご指摘いただいた通り、ScleenFlip();を記述して描画画面の反転を行ったら問題解消されました。

見やすいコードの書き方や設計などはこれからもっとコードを記述しながら勉強しようと思います。
見にくい文ながらもこたえていただき、ありがとうございました。

Re: for文の繰り返し回数

Posted: 2011年10月22日(土) 01:26
by 沖 滉均
ふぅ さんが書きました:ご指摘いただいた通り、ScleenFlip();を記述して描画画面の反転を行ったら問題解消されました。
本当に解決していますか?その文だけですと、入れた箇所に問題があるように思えます。
もしよろしければ、解決したコードも載せていただけると良いのですがいかがでしょう?

Re: for文の繰り返し回数

Posted: 2011年10月22日(土) 01:27
by Dixq (管理人)
ScreenFlipを記述したとのことですが、もし以下のように対応されたのだとしたら

コード:

void seikai(){
	for( i=0; i<60; i++ ){//一秒間表示
		DrawGraph( 0, 0, TRUE_A, TRUE );//正解の画像の表示
		ScreenFlip();
	}
}
これは間違いです。
ゲームプログラムはどこか特定の箇所で処理を止めてはダメです。
この間何かあっても他に何もすることが出来ません。
今回は1秒ですが、もし1分待機したい場合はどうなるでしょう。
また、キャラクタが100体いる時は、100体並行して同時に処理する必要があります。
よって毎回処理はメインループに返す必要があり、
ScreenFlip(), ProcessMessage(), ClearDrawScreen()の3大関数が複数個所に出現したらそのプログラムは間違った設計である可能性が高いです。

これらの関数は1か所にしか書かずに実現出来る設計を検討してみて下さい。

Re: for文の繰り返し回数

Posted: 2011年10月22日(土) 01:44
by ISLe
ゲームプログラムの作り方の問題もありますが、他にもいろいろ問題があります。

試しに30秒くらいループするようにして、そのあいだにウィンドウを操作してみてください。

Re: for文の繰り返し回数

Posted: 2011年10月22日(土) 03:04
by ふぅ
Dixq (管理人) さんが書きました:よって毎回処理はメインループに返す必要があり、
ScreenFlip(), ProcessMessage(), ClearDrawScreen()の3大関数が複数個所に出現したらそのプログラムは間違った設計である可能性が高いです。

これらの関数は1か所にしか書かずに実現出来る設計を検討してみて下さい。
たびたび失礼します。
数値を入力する関数とif文で繰り返し回数を判定させる関数の記述に変えましたがいかがでしょう?

コード:

void GameMain_Mondai1(){

	static int count;
	count++;
	if(count==1){Lv1[0] = GetRand(9999);}//4桁の数をランダムで表示
	if(Lv1[0]<=1000){Lv1[0] = 1000+Lv1[0];}//数が3ケタだった場合4桁に修正する。
	if(count<=60){DrawFormatString( 300,230,GetColor(255,255,255),"%d",Lv1[0]);}//一秒たつまで問題を表示
	if(count>=60){count=0;
					mode=4;}}//問題を表示し終わったらカウンターを初期化し、回答処理へ

void GameMain_answer1(){
	static int count;
		DrawFormatString( 230,180,GetColor(255,255,255),"4桁の数字を入力してください");
		LvA = KeyInputNumber( 230 , 200 , 9999 , 0 , FALSE );//9999の数まで数値を受け付ける
		               mode=27;
					   }
void GameMain_A1(){
	static int count;
	/*正解*/		if(LvA == Lv1[0]){DrawGraph(0,0,TRUE_A,TRUE);
										count++;//60加算するまで繰り返し
							if(count>=60){stage=stage+1;//正解数加算
							/*正解*/		if(stage==3)//三回正解したら
														{count=0;//カウンターを初期化
														mode=1;//レベル振り分けクラスに戻る
														}
								else{
												count=0;//カウンターの初期化
												mode=3;//もう一度出題
												
									}
							             }
												
						             }
	/*不正解*/		else {DrawGraph(0,0,NO_A,TRUE);//不正解の画像の表示
										count++;//60加算するまで繰り返し
								if(count>=60){LIFE= LIFE-1;//ライフゲージを1減らす。
											if(LIFE==0)//ライフが0の場合
											 {count=0;
											  mode=25;
										     }
									else{count=0;
										 mode=3;
										}
									         }
			              }
				  }


Re: for文の繰り返し回数

Posted: 2011年10月22日(土) 07:48
by box
別の場所でも指摘があったようですが、インデントにポリシーが全く感じられず、
コードがひじょうに読みづらいです。
せめて、下記程度には書けないものでしょうか。最良の書き方かどうかはわかりませんけれど。

コード:

void GameMain_Mondai1()
{
    static int count;
    
    count++;
    
    //4桁の数をランダムで表示
    if (count == 1) {
        Lv1[0] = GetRand(9999);
    }
    
    //数が3ケタだった場合4桁に修正する。
    if (Lv1[0] <= 1000) {
        Lv1[0] = 1000 + Lv1[0];
    }
    
    //一秒たつまで問題を表示
    if (count <= 60) {
        DrawFormatString(300, 230, GetColor(255,255,255), "%d", Lv1[0]);
    }
    
    //問題を表示し終わったらカウンターを初期化し、回答処理へ
    if (count >= 60) {
        count = 0;
        mode  = 4;
    }
}
 
void GameMain_answer1()
{
    static int count;
    
    //9999の数まで数値を受け付ける
    DrawFormatString(230, 180, GetColor(255,255,255), "4桁の数字を入力してください");
    LvA  = KeyInputNumber(230, 200, 9999, 0, FALSE );
    mode = 27;
}

void GameMain_A1()
{
    static int count;
    
    /*正解*/
    if (LvA == Lv1[0]) {
        DrawGraph(0, 0, TRUE_A, TRUE);
        count++;                        //60加算するまで繰り返し
        if (count >= 60) {
            stage = stage + 1;          //正解数加算
            /*正解*/
            if (stage == 3) {           //三回正解したら
                count = 0;              //カウンターを初期化
                mode  = 1;              //レベル振り分けクラスに戻る
            }
            else {
                count = 0;              //カウンターの初期化
                mode  = 3;              //もう一度出題
            }
        }
    }
    
    /*不正解*/
    else {
        DrawGraph(0, 0, NO_A, TRUE);    //不正解の画像の表示
        count++;                        //60加算するまで繰り返し
        if (count >= 60) {
            LIFE = LIFE - 1;            //ライフゲージを1減らす。
            if (LIFE == 0) {            //ライフが0の場合
                count = 0;
                mode  = 25;
            }
            else {
                count = 0;
                mode  = 3;
            }
        }
    }
}

Re: for文の繰り返し回数

Posted: 2011年10月22日(土) 11:09
by Dixq (管理人)
上でも言った通りですが、インデントがあそこまで狂っているのは何故でしょうか?
何かエディタの都合?
それともあのインデントが一番自分にとって見やすいから意図的にしているのでしょうか?

もしVc++で書かれていれば、自分で意図的にインデントしなくても、
ある程度書いて改行すれば、あるべき位置まで自動でインデントされます。
また、書き終わってからでも、Ctrl+A,K,Fを押せば自動インデントされます。

分かり易い、見やすいコードを書くようにしてみてください。

提示されたコードについてですが、GameMain_Mondai1関数内で、期待の表示がされるかされないかで言えば、されると思います。
ただ、やはり行うべきではない処理があちこちにみられます。
ざっと見て、思った事を書いてみます。

・KeyInputNumberを使うとそこで処理が止まる。MakeKeyInputを使うなど処理は止めないようにすべき。
・大文字のみの変数を使わない。大文字のみは普通定義に使います。
・static変数でカウンタを作らない。外から初期化が出来ません。ある瞬間関数コールをやめると、次に呼ばれた時に途中からの状態になります。
・ c=0; for(c=0;... は同じことを二回している。
・iやaなどの変数をグローバル変数にすべきではない。iは普通ループカウンタなどに使用します。
・変数名の命名規則が分からない、また、変数名を見ても何の役割をするか分からない。(dは例えばSelectingNumberなどにしてみては)
・ループは期待の動作+1回回っている。
・マジックナンバーが多くて、何をしているのか分かりにくい。欲を言えばenumやdefineを使って分かり易いコードに
・似たような振る舞いをする関数が複数ある。共通化出来る物が無いか検討する。
・case 0内でDXライブラリの主要な処理を呼ばない。終了処理はbreakして、最後に書いてあるコードを通るようにすればいい。



・・とまぁ偉そうに言っていますが、私も最初は今では考えられないようなコードを沢山書いていました。
沢山コードを書き、人から沢山指摘を受け、真摯にそれを改善して、成長するものだと思いますので、地道に頑張って下さい。
少しコードを書いては、レビュー(人にコードを見てもらい、指摘を受ける)してもらうためにトピックを立てて頂いて結構ですので、より良いコードが書けるよう頑張って下さい。

Re: for文の繰り返し回数

Posted: 2011年10月22日(土) 15:07
by ふぅ
box さんが書きました:別の場所でも指摘があったようですが、インデントにポリシーが全く感じられず、
コードがひじょうに読みづらいです。
せめて、下記程度には書けないものでしょうか。最良の書き方かどうかはわかりませんけれど。
インデントについて。
汚く、とか、見づらく、とかインデントして見やすくする気がないわけでもありません。
始めてまだ二か月で、コードを見てもらうのもほとんど初めてです。
見やすい書き方などほぼ無知に等しかったものですから。
そんなにバカにしないで、お手柔らかにお願いします・・・

Dixq (管理人) さんが書きました:上でも言った通りですが、インデントがあそこまで狂っているのは何故でしょうか?
何かエディタの都合?
それともあのインデントが一番自分にとって見やすいから意図的にしているのでしょうか?

もしVc++で書かれていれば、自分で意図的にインデントしなくても、
ある程度書いて改行すれば、あるべき位置まで自動でインデントされます。
また、書き終わってからでも、Ctrl+A,K,Fを押せば自動インデントされます。

分かり易い、見やすいコードを書くようにしてみてください。
参考にさせていただきます。
Dixq (管理人) さんが書きました: ・KeyInputNumberを使うとそこで処理が止まる。MakeKeyInputを使うなど処理は止めないようにすべき。
・大文字のみの変数を使わない。大文字のみは普通定義に使います。
・static変数でカウンタを作らない。外から初期化が出来ません。ある瞬間関数コールをやめると、次に呼ばれた時に途中からの状態になります。
・ c=0; for(c=0;... は同じことを二回している。
・iやaなどの変数をグローバル変数にすべきではない。iは普通ループカウンタなどに使用します。
・変数名の命名規則が分からない、また、変数名を見ても何の役割をするか分からない。(dは例えばSelectingNumberなどにしてみては)
・ループは期待の動作+1回回っている。
・マジックナンバーが多くて、何をしているのか分かりにくい。欲を言えばenumやdefineを使って分かり易いコードに
・似たような振る舞いをする関数が複数ある。共通化出来る物が無いか検討する。
・case 0内でDXライブラリの主要な処理を呼ばない。終了処理はbreakして、最後に書いてあるコードを通るようにすればいい。

わかりにくい動作などに関しては、もう少し最初から人に見られるつもりで書いてみます。
人に見られてわかりにくいということはあとで自分が見てもわかりにくいでしょうから。

細かなご指摘ありがとうございました。

Re: for文の繰り返し回数

Posted: 2011年10月22日(土) 17:15
by box
より適切であろうと私が考えている書き方の例を示しただけです。
他意はありません。

まあ、とにかく、他の人が書いたコードをよく見て、
どういう書き方をしているかを勉強することを強くおすすめします。