whileについて

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

whileについて

#1

投稿記事 by ジャンたん » 13年前

今、イエスかノーを選択する関数を作っているのですが、
二回目の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について

#2

投稿記事 by エクレ » 13年前

ScreenFlip()なしで高速ループしてるからじゃないですか

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 15年前
住所: 北海道札幌市
連絡を取る:

Re: whileについて

#3

投稿記事 by Dixq (管理人) » 13年前

ゲームプログラミングの館で紹介している「やってはいけない処理」の実装になってしまっています。
http://dixq.net/g/h_11.html

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

ジャン

Re: whileについて

#4

投稿記事 by ジャン » 13年前

ループ中にループするとこんな風になっちゃうんですか。
とりあえず、こうしたら正常に動きましたけど、これはまちがった処理なんでしょうか?

コード:

 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;
		}
	}
ゲーム中で選択肢を選ぶ時に処理を止めるにはどのようにすればよいのでしょう

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

Re: whileについて

#5

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

バグのもとですから、インデントを揃えてくださいね。

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

よくこの掲示板でもよく分からない動きになったと相談されますが、この形で書かれたプログラムを直すのが一番困難です。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

Ghost X

Re: whileについて

#6

投稿記事 by Ghost X » 13年前

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について

#7

投稿記事 by エクレ » 13年前

>ゲーム中で選択肢を選ぶ時に処理を止めるにはどのようにすればよいのでしょう

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

ジャンたん

Re: whileについて

#8

投稿記事 by ジャンたん » 13年前

こりあえずのソースです。
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について

#9

投稿記事 by ジャンたん » 13年前

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;
		}
	}

 }

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

Re: whileについて

#10

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

gotoを常用的に使うのは止めましょう。
それとインデント(字下げ)が不正確なのと、if文の{の位置が統一性のないのもバグの原因となります。

こういう処理は状態遷移(せんい)と言う考えを使います。
ステートマシン図0.png
ステートマシン図0.png (23.73 KiB) 閲覧数: 3341 回
この様に状態を移り変わらせる事で目的の動作をさせます。
色々書き方があるのですが一例として
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;
}
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ジャン

Re: whileについて

#11

投稿記事 by ジャン » 13年前

すごく丁寧な解説ありがとうございます。
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;
		}
	}

 }

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

Re: whileについて

#12

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

インデント字下げがだんだんとひどくなってますね。すごく読みづらいです。
インデントがよく分からなかったら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を離したことが管理できます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ジャンたん

Re: whileについて

#13

投稿記事 by ジャンたん » 13年前

なるほどそうすればよかったのですか。
みなさまのおかげで基礎が深まりました。
ありがとうございます。

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

Re: whileについて

#14

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

ジャンたん さんが書きました:なるほどそうすればよかったのですか。
みなさまのおかげで基礎が深まりました。
ありがとうございます。
ここのルールなのですが解決したコードを貼っていただくようにお願いします。
後から同じ問題に突き当たった人のための参考コードとさせて頂きます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ジャンたん

Re: whileについて

#15

投稿記事 by ジャンたん » 13年前

コード貼っておきますね

コード:

#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;
		}
	}
}

閉鎖

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