ページ 11

whileについて

Posted: 2012年8月03日(金) 23:12
by ジャンたん
今、イエスかノーを選択する関数を作っているのですが、
二回目のwhileでフリーズしてしまいます。
何故ですか?
よろしくおねがいします。

コード:

while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 ){//ここから繰り返し

        Keyboard_Update();
	
	KeyInputString( 0 , 0 , 20 , pn , TRUE ) ;
	
	while(1)
	{
		
		if( ProcessMessage() == -1 )
		{
			 break ;	// エラーが発生したらループを抜ける
		}

		DrawFormatString( 0 , 15 ,GetColor( 255 , 255 , 255 ), "名前は%sでよろしいですか?" , pn ) ;

		yesno(&x);

		if(Keyboard_Get( KEY_INPUT_RETURN) ==1){
		break;
		}
	}
}

Re: whileについて

Posted: 2012年8月03日(金) 23:16
by エクレ
ScreenFlip()なしで高速ループしてるからじゃないですか

Re: whileについて

Posted: 2012年8月03日(金) 23:24
by Dixq (管理人)
ゲームプログラミングの館で紹介している「やってはいけない処理」の実装になってしまっています。
http://dixq.net/g/h_11.html

メインループの中に、処理を止めるループを書いてはいけません。
プログラムの処理を一つずつ考えてみましょう。
中のループはキーボードの状態をUpdateすることなくぐるぐるまわります。
つまり、1週目でループを抜けなければ永遠に
if(Keyboard_Get( KEY_INPUT_RETURN) ==1){
を満たすことが無くなります。

Re: whileについて

Posted: 2012年8月04日(土) 00:32
by ジャン
ループ中にループするとこんな風になっちゃうんですか。
とりあえず、こうしたら正常に動きましたけど、これはまちがった処理なんでしょうか?

コード:

 while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 ){//ここから繰り

   	Keyboard_Update();
	
	KeyInputString( 0 , 0 , 20 , pn , TRUE ) ;
	
	while(1)
	{
		// メッセージ処理
		if( ProcessMessage() == -1 )
		{
			 break ;	// エラーが発生したらループを抜ける
		}
		if( ScreenFlip() == -1 )
		{
			 break ;	
		}
		if( ClearDrawScreen() == -1 )
		{
			 break ;	
		}
		Keyboard_Update();
		DrawFormatString( 0 , 15 ,GetColor( 255 , 255 , 255 ), "名前は%sでよろしいですか?" , pn ) ;
		yesno(&x);
		if(Keyboard_Get( KEY_INPUT_RETURN) ==1){
		break;
		}
	}
ゲーム中で選択肢を選ぶ時に処理を止めるにはどのようにすればよいのでしょう

Re: whileについて

Posted: 2012年8月04日(土) 00:41
by softya(ソフト屋)
バグのもとですから、インデントを揃えてくださいね。

この書き方の悪い点は拡張性が著しく悪いところでしょうか。
あとセーブロードや終了処理やゲームオーバー処理の記述がトリッキーになり高い確率でバグになります。
後々すごく困ることに成るのは間違い無い悪い書き方も筆頭だと思いますよ。

よくこの掲示板でもよく分からない動きになったと相談されますが、この形で書かれたプログラムを直すのが一番困難です。

Re: whileについて

Posted: 2012年8月04日(土) 01:02
by Ghost X
Dixq (管理人) さんが書きました:メインループの中に、処理を止めるループを書いてはいけません。
とは、どういうことか分かりますか。

基本的に、

コード:

while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 ){//ここから繰り返し
はまず通って、そのあとはずっと

コード:

while(1)
で止まっているってことです。
この場合、whileループは一つで十分です。

コード:

while(1){//ここから繰り返し
	if(ScreenFlip()==-1 || ProcessMessage()==-1 || ClearDrawScreen()==-1)break;
	Keyboard_Update();
	KeyInputString(0,0,20,pn,TRUE);
	DrawFormatString(0,15,GetColor(255,255,255),"名前は%sでよろしいですか?",pn);
	yesno(&x);
	if(Keyboard_Get(KEY_INPUT_RETURN)==1)break;
}
yesno関数がどうなってるか良く分からないので、なんとも言えませんが。

Re: whileについて

Posted: 2012年8月04日(土) 01:12
by エクレ
>ゲーム中で選択肢を選ぶ時に処理を止めるにはどのようにすればよいのでしょう

常にループしているので、止まってるように描画するだけです。
処理自体は止まらずにずっとループしてます

Re: whileについて

Posted: 2012年8月04日(土) 02:01
by ジャンたん
こりあえずのソースです。
YESを選んでから"あなたの名前は%sです"と表示させたいのです。

コード:

#include "DxLib.h"
#include "Keyboard.h"

void yesno(int *x);

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
        ChangeWindowMode(TRUE),DxLib_Init(),SetDrawScreen( DX_SCREEN_BACK );

		char pn[21];
		strcpy( pn , "aaaa" ) ;//とりあえずの名前
	
   while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 ){//ここから繰り返し

	int x=185;
    	Keyboard_Update();
	NO://goto文
	KeyInputString( 0 , 0 , 20 , pn , TRUE ) ;//名前を入力
	while(1)
	{
		if(ScreenFlip()==-1 || ProcessMessage()==-1 || ClearDrawScreen()==-1)break;
		Keyboard_Update();
		DrawFormatString( 0 , 15 ,GetColor( 255 , 255 , 255 ), "名前は%sでよろしいですか?" , pn ) ;
		yesno(&x);
		if(Keyboard_Get( KEY_INPUT_RETURN ) ==1){
			if(x==235)
			{
			goto NO;
			}
		break;
		}		
	}

	DrawString( 0 , 30 , "あなたの名前は" , GetColor( 255 , 255 , 255 ) ) ;
	DrawFormatString( 0 , 45 , GetColor( 255 , 255 , 255 ),"%sです。",pn ) ;
        
	// キー入力待ち	
	WaitKey() ;

	DxLib_End() ;		// DXライブラリ使用の終了処理

	return 0 ;		// ソフトの終了
}

 void yesno(int *x)
 {	
	DrawString( 200 ,300  , "YES" , GetColor( 255 , 255 , 255 ) ) ;
	DrawString( 250 ,300  , "NO" , GetColor( 255 , 255 , 255 ) ) ;
	DrawString( *x ,300  , "→" , GetColor( 255 , 255 , 255 ) ) ;
	if( Keyboard_Get( KEY_INPUT_RIGHT   ) ==1 ){ // 右キーが押されていたら
		if(*x==185){
			*x=235;
		}
		else{
			*x=185;
		}
	}

 }

Re: whileについて

Posted: 2012年8月04日(土) 02:23
by ジャンたん
16行目のgotoがおかしくなってますね
      

コード:

NO:
	KeyInputString( 0 , 0 , 20 , pn , TRUE ) ;//名前を入力
	while(1)
	{
		if(ScreenFlip()==-1 || ProcessMessage()==-1 || ClearDrawScreen()==-1)break;
		Keyboard_Update();
		DrawFormatString( 0 , 15 ,GetColor( 255 , 255 , 255 ), "名前は%sでよろしいですか?" , pn ) ;
		yesno(&x);
		if(Keyboard_Get( KEY_INPUT_RETURN ) ==1){
			if(x==235)
			{
			goto NO;
			}
		break;
		}		
	}

	DrawString( 0 , 30 , "あなたの名前は" , GetColor( 255 , 255 , 255 ) ) ;
	DrawFormatString( 0 , 45 , GetColor( 255 , 255 , 255 ),"%sです。",pn ) ;
        
	// キー入力待ち	
	WaitKey() ;

	DxLib_End() ;		// DXライブラリ使用の終了処理

	return 0 ;		// ソフトの終了
}

 void yesno(int *x)
 {	
	DrawString( 200 ,300  , "YES" , GetColor( 255 , 255 , 255 ) ) ;
	DrawString( 250 ,300  , "NO" , GetColor( 255 , 255 , 255 ) ) ;
	DrawString( *x ,300  , "→" , GetColor( 255 , 255 , 255 ) ) ;
	if( Keyboard_Get( KEY_INPUT_RIGHT   ) ==1 ){ // 右キーが押されていたら
		if(*x==185){
			*x=235;
		}
		else{
			*x=185;
		}
	}

 }

Re: whileについて

Posted: 2012年8月04日(土) 11:19
by softya(ソフト屋)
gotoを常用的に使うのは止めましょう。
それとインデント(字下げ)が不正確なのと、if文の{の位置が統一性のないのもバグの原因となります。

こういう処理は状態遷移(せんい)と言う考えを使います。
ステートマシン図0.png
ステートマシン図0.png (23.73 KiB) 閲覧数: 3346 回
この様に状態を移り変わらせる事で目的の動作をさせます。
色々書き方があるのですが一例として
int state;
と言う変数を用意して
あと状態をenumで用意します。
enum { STATE_NAME_INPUT,STATE_NAME_KAKUNIN,STATE_NEXT };
初期値も設定しておきましょうか。上の変数宣言を直して
int state = STATE_NAME_INPUT;
とします。

あとは、DXライブラリの基本whileループの中にswtich~case文で各状態の処理を書けば良いです。

コード:

switch(state) {
case STATE_NAME_INPUT:	//名前の入力処理
	//	名前を入力してもらう。
	//	stateをSTATE_NAME_KAKUNINに変更。
	break;
case STATE_NAME_KAKUNIN://名前の確認
	//	名前を表示
	//	yes/noを選択してもらう
	//	yesならstateをSTATE_NEXTに変更。
	//	noならstateをSTATE_NAME_INPUTに変更。
	break;
case STATE_NEXT:		//その後の処理
	break;
}

Re: whileについて

Posted: 2012年8月04日(土) 12:34
by ジャン
すごく丁寧な解説ありがとうございます。
27行目のKEY_INPUT_RETURNをKEY_INPUT_Zに変えました。
名前を入力すると、27行目のKEY_INPUT_RETURNが続けて認識されるみたいなのですが
そうならないようにするにはどうしたらよいのですか?

コード:

#include "DxLib.h"
#include "Keyboard.h"

void yesno(int *x);

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
    ChangeWindowMode(TRUE),DxLib_Init(),SetDrawScreen( DX_SCREEN_BACK );

	char pn[21];
	strcpy( pn , "aaaa" ) ;//とりあえずの名前
	int state;
	enum { STATE_NAME_INPUT,STATE_NAME_KAKUNIN,STATE_NEXT };
	state=STATE_NAME_INPUT;
	int x=185;
	
while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 ){//ここから繰り返し
    Keyboard_Update();

	switch(state) {
	case STATE_NAME_INPUT:  //名前の入力処理
		KeyInputString( 0 , 0 , 20 , pn , TRUE ) ;//名前を入力
		state=STATE_NAME_KAKUNIN;
		break;
	case STATE_NAME_KAKUNIN://名前の確認
		DrawFormatString( 0 , 15 ,GetColor( 255 , 255 , 255 ), "名前は%sでよろしいですか?" , pn ) ;
		yesno(&x);//  yes/noを選択してもらう
		if(Keyboard_Get( KEY_INPUT_Z) ==1){
			if(x==185){
				state=STATE_NEXT;
			}
			if(x==235){
				state=STATE_NAME_INPUT;
			}
		}
		break;
	case STATE_NEXT:        //その後の処理
		DrawString( 0 , 30 , "あなたの名前は" , GetColor( 255 , 255 , 255 ) ) ;
		DrawFormatString( 0 , 45 , GetColor( 255 , 255 , 255 ),"%sです。",pn ) ;
		break;
	}
}	
	     	
	WaitKey() ;		// キー入力待ち	

	DxLib_End() ;		// DXライブラリ使用の終了処理

	return 0 ;		// ソフトの終了
}

void yesno(int *x){	
	DrawString( 200 ,300  , "YES" , GetColor( 255 , 255 , 255 ) ) ;
	DrawString( 250 ,300  , "NO" , GetColor( 255 , 255 , 255 ) ) ;
	DrawString( *x ,300  , "→" , GetColor( 255 , 255 , 255 ) ) ;
	if( Keyboard_Get( KEY_INPUT_RIGHT) ==1 ){ // 右キーが押されていたら
		if(*x==185){
			*x=235;
		}
		else{
			*x=185;
		}
	}
	if( Keyboard_Get( KEY_INPUT_LEFT) ==1 ){ 
		if(*x==185){
			*x=235;
		}
		else{
			*x=185;
		}
	}

 }

Re: whileについて

Posted: 2012年8月04日(土) 13:03
by softya(ソフト屋)
インデント字下げがだんだんとひどくなってますね。すごく読みづらいです。
インデントがよく分からなかったらgoogleで検索してください。それでも分からなかったら質問してくださいね。

それは押されている間はKeyboard_Get( KEY_INPUT_RETURN)条件が成立し続けるからだと思います。
KeyInputString()が最後にKEY_INPUT_RETURNで終わると思いますのでその後のRETURNを離すまでは成立状態なんですが、これを避けるにはちょっと面倒な事が必要です。
方法の1つとしては、
int ReturnReleaseFlag = 0;
を用意して
KeyInputString()
を抜けた直後に
ReturnReleaseFlag=1;
とします。
このフラグを使って、
if(Keyboard_Get(KEY_INPUT_RETURN) ==1){
の時はReturnReleaseFlagが1ならstateの変更をしては行けません。上記if文がelseならRETURNを離したことになるので、ReturnReleaseFlagを0にします。
これでRETURNを離したことが管理できます。

Re: whileについて

Posted: 2012年8月04日(土) 13:35
by ジャンたん
なるほどそうすればよかったのですか。
みなさまのおかげで基礎が深まりました。
ありがとうございます。

Re: whileについて

Posted: 2012年8月04日(土) 13:38
by softya(ソフト屋)
ジャンたん さんが書きました:なるほどそうすればよかったのですか。
みなさまのおかげで基礎が深まりました。
ありがとうございます。
ここのルールなのですが解決したコードを貼っていただくようにお願いします。
後から同じ問題に突き当たった人のための参考コードとさせて頂きます。

Re: whileについて

Posted: 2012年8月05日(日) 01:25
by ジャンたん
コード貼っておきますね

コード:

#include "DxLib.h"
#include "Keyboard.h"

void yesno(int *x);

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
	ChangeWindowMode(TRUE),DxLib_Init(),SetDrawScreen( DX_SCREEN_BACK );

	char pn[21];
	strcpy( pn , "aaaa" ) ;//とりあえずの名前
	int state;
	enum { STATE_NAME_INPUT,STATE_NAME_KAKUNIN,STATE_NEXT };
	state=STATE_NAME_INPUT;
	int x=185;
	int ReturnReleaseFlag = 0;
	
	while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 ){//ここから繰り返し
		Keyboard_Update();

		switch(state) {
			case STATE_NAME_INPUT:  //名前の入力処理
				KeyInputString( 0 , 0 , 20 , pn , TRUE ) ;//名前を入力
				ReturnReleaseFlag = 1;
				state=STATE_NAME_KAKUNIN;
				break;
			case STATE_NAME_KAKUNIN://名前の確認
				DrawFormatString( 0 , 15 ,GetColor( 255 , 255 , 255 ), "名前は%sでよろしいですか?" , pn ) ;
				yesno(&x);//  yes/noを選択してもらう
				if(Keyboard_Get( KEY_INPUT_RETURN) ==1&&ReturnReleaseFlag ==0){
					if(x==185){
						state=STATE_NEXT;
					}
					if(x==235){
						state=STATE_NAME_INPUT;
					}
				}else{
					ReturnReleaseFlag = 0;
				}
				break;
			case STATE_NEXT:        //その後の処理
				DrawString( 0 , 30 , "あなたの名前は" , GetColor( 255 , 255 , 255 ) ) ;
				DrawFormatString( 0 , 45 , GetColor( 255 , 255 , 255 ),"%sです。",pn ) ;
				break;
		}
	}	     	
	WaitKey() ;		// キー入力待ち	
	DxLib_End() ;	// DXライブラリ使用の終了処理
	return 0 ;		// ソフトの終了
}

void yesno(int *x){	
	DrawString( 200 ,300  , "YES" , GetColor( 255 , 255 , 255 ) ) ;
	DrawString( 250 ,300  , "NO" , GetColor( 255 , 255 , 255 ) ) ;
	DrawString( *x ,300  , "→" , GetColor( 255 , 255 , 255 ) ) ;
	if( Keyboard_Get( KEY_INPUT_RIGHT) ==1 ){ // 右キーが押されていたら
		if(*x==185){
			*x=235;
		}else{
			*x=185;
		}
	}
	if( Keyboard_Get( KEY_INPUT_LEFT) ==1 ){ 
		if(*x==185){
			*x=235;
		}else{
			*x=185;
		}
	}
}