選択画面の応用 バグ修正

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

選択画面の応用 バグ修正

#1

投稿記事 by ののき » 14年前

以前、選択画面の応用というトピックを立てた者ですが、バグに気付き、修正の方法を伺いたく参りました。

バグは、選択画面の状態で最初に選ばれている選択肢から下にスクロールすると、一瞬で元の選択肢にもどってきてしまうものです。
また、これに加えて selectnum=0のときにzキーを押す(ゲームスタートが選ばれた状態)と、表示画面が切り替わる動作を付け加えたいのですが、この動作はif( Key[ KEY_INPUT_Z] == 1 )をif( Key[ KEY_INPUT_Z] == 1 )&&(selectNum=0)にただ単に変えればいいのでしょうか?
selectNumはvoid show menuの下部にあるので、未定義の状態になり、実行できないと予想しています。この部分はselectNumをvoid show menuの
最上部に書くことで解決されるのでしょうか。

どうぞよろしくお願い致します。

コード:

#include "DxLib.h"
 
enum STATUS{ //列挙型(enum)は、一定の範囲の値を保持するためのデータ型です。

    STATUS_SHOW_MENU,
    STATUS_SHOW_CHARA,
};
 
int Key[256]; // キーが押されているフレーム数を格納する
STATUS Status = STATUS_SHOW_MENU;
 
// キーの入力状態を更新する
int gpUpdateKey(){
    char tmpKey[256]; // 現在のキーの入力状態を格納する
    GetHitKeyStateAll( tmpKey ); // 全てのキーの入力状態を得る
    for( int i=0; i<256; i++ ){ 
        if( tmpKey[i] != 0 ){ // i番のキーコードに対応するキーが押されていたら
            Key[i]++;     // 加算
        } else {              // 押されていなければ
            Key[i] = 0;   // 0にする
        }
    }
    return 0;
}
 


void ShowMenu(){
	
	if( Key[ KEY_INPUT_Z] == 1 ){
        Status = STATUS_SHOW_CHARA;
    }



	// メニュー項目の表示に必要な構造体を用意する
	typedef struct{
        int x, y;       // 座標格納用変数
        char name[128]; // 項目名格納用変数
	} MenuElement_t ;

	// メニュー項目要素を5つ作る
        MenuElement_t MenuElement[5]={
                {  80, 100, "ゲームスタート" }, // タグの中身の順番で格納される。xに80が、yに100が、nameに"ゲームスタート"が
                { 100, 150, "おまけ" },
                { 100, 200, "ヘルプ" },
                { 100, 250, "コンフィグ" },
                { 100, 300, "ゲーム終了" },
        };
        int SelectNum = 0; // 現在の選択番号

				
                // 計算フェーズ 
			 

                if( Key[ KEY_INPUT_DOWN ] == 1 ){ // 下キーが押された瞬間だけ処理

                        SelectNum = ( SelectNum + 1 ) % 5; // 現在の選択項目を一つ下にずらす(ループする)

                        for( int i=0; i<5; i++ ){              // メニュー項目数である5個ループ処理
                                if( i == SelectNum ){          // 今処理しているのが、選択番号と同じ要素なら
                                        MenuElement[i].x = 80; // 座標を80にする
                                }
								else {                       // 今処理しているのが、選択番号以外なら
                                        MenuElement[i].x = 100;// 座標を100にする
                                }
                         }
                
				}
                // 描画フェーズ

                for( int i=0; i<5; i++ ){ // メニュー項目を描画
                        DrawFormatString( MenuElement[i].x, MenuElement[i].y, GetColor(255,255,255), MenuElement[i].name );
                }



        }
			


void ShowChara(){
    if( Key[ KEY_INPUT_X] == 1 ){
        Status = STATUS_SHOW_MENU;
    }
    DrawString(0,0,"キャラ表示中",GetColor(255,255,255));
}
 
int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
    ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); 
 
    while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 && gpUpdateKey()==0 ){
        switch( Status ){
            case STATUS_SHOW_MENU:
                ShowMenu();
                break;
            case STATUS_SHOW_CHARA:
                ShowChara();
                break;
        }
    }     //while間のループは1フレームごとに描写、メッセージの描写、裏画面処理をしている。そして、show Menu、つまりxを押すとその処理が実行される
 
    DxLib_End();
    return 0;
}



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

Re: 選択画面の応用 バグ修正

#2

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

まず、MenuElementとSelectNum が関数に突入する度に初期化されるのでメニュー選択としての機能を果たしていません。
状態が保持されるのは1/60秒間だけです。
これは、ローカル変数の初期化が毎回行われるルールだからです。方法としてはローカル変数をやめるかstaticを付けるの2つの方法が考えられます。
そのものズバリの回答は書きませんので、MenuElementとSelectNum をいつ初期化するのかよく検討の上、最適の方法を選んでください。

もう一つの件は試してみればわかります。何かが壊れるわけではないで、経験のためにぜひ実験してみてください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ののき

Re: 選択画面の応用 バグ修正

#3

投稿記事 by ののき » 14年前

int select Numの位置を変えずにint static select Numに変更したところ、若干の改善が見られました。
次の操作を行うまでにselect Numの値を固定するには、どこに着目すべきですか、よろしければご指導お願い致します。

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

Re: 選択画面の応用 バグ修正

#4

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

前にも書いたとおりMenuElementも問題です。
何が問題なのか、想像でも良いので書いてみてください。

[追記]
あと慣例として
int static select Num
の書き方はしませんので
static int selectNum
と書いてくださいね。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ののき

Re: 選択画面の応用 バグ修正

#5

投稿記事 by ののき » 14年前

menuElementの問題はstatic int select Numで値が固定されたのに、process message のループのせいで一回ループを繰り返すごとにもとに戻ってしまうからでしょうか。
正直に言って、答えに自信はありません。

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

Re: 選択画面の応用 バグ修正

#6

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

これはC言語として基本的な事柄なので、しっかりと把握してください。
デバッグ技法の一番初歩のやり方ですが、数値を見ることで状況を把握する方法をご紹介します。

コード:

				clsDx();
                for( int i=0; i<5; i++ ){ // メニュー項目を描画
		                printfDx( " MenuElement[%d].x = %d, MenuElement[%d].y = %d\n" , i, MenuElement[i].x, i, MenuElement[i].y ) ;
                        DrawFormatString( MenuElement[i].x, MenuElement[i].y, GetColor(255,255,255), MenuElement[i].name );
                }
あと数値の変化が早すぎて分かりづらかったら
WaitTimer(200);
をwhileの直後に入れてください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ののき

Re: 選択画面の応用 バグ修正

#7

投稿記事 by ののき » 14年前

返信遅れて大変申し訳ありません。バグを修正するのにかなり手間取っていました。
返信をもらってから、いろいろと試してみたのですが、

// メニュー項目要素を5つ作る
MenuElement_t MenuElement[5] を変更し、

// メニュー項目要素を5つ作る
static MenuElement_t MenuElement[5]

にしたところ、無事正常に動作いたしました。

原因はMenu Elementがループするたびに初期化されることだとは分かったのですが、なぜ、// メニュー項目要素を5つ作る
のコードにstatic をつけると、初期化されないのかが今でもいまいち理解できません。

推測するに、 // メニュー項目要素を5つ作る の処理は列挙してある構造体の数字を変更して、画面描写を行っている。
この部分を初期化してしまうと一番上のゲームスタートという選択肢に強制的に移動してしまう。
初期化せずにいれば、下の選択肢にスクロールしても画面はそのままということなのでしょうか。

だとすれば、私は
for( int i=0; i<5; i++ ){ // メニュー項目数である5個ループ処理
if( i == SelectNum ){ // 今処理しているのが、選択番号と同じ要素なら
MenuElement.x = 80; // 座標を80にする
}
else { // 今処理しているのが、選択番号以外なら
MenuElement.x = 100;// 座標を100にする
}
}
の部分で
MenuElement.x = 100;// 座標を100にする
が独立していると勘違いしていたことになり、この座標の変更が // メニュー項目要素を5つ作る の
値に反映することに気付いていなかったから、バグを修正することが今まで出来なかったということなのでしょうか。

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

Re: 選択画面の応用 バグ修正

#8

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

まず理解して欲しいのは変数の寿命ですね。
staticは変数の寿命をプログラムの開始から終了までとします。それに対して関数の内部変数は関数が呼び出されて関数を抜けるまでです。
変数の寿命が違うので変数の初期化タイミングも違います。
staticは、プログラム開始時に初期化されその後は2度と初期化されません。それに対して関数の内部変数は関数が呼び出される度に生成されるので初期化も毎回行われます。
つまり、ただの関数の内部変数だとShowMenu()が呼び出される度にMenuElementは初期化されx座標が80,100,100,100,100に初期化されて、描画フェーズではその初期化された座標で表示されると言うことです。
これがキー入力がちゃんと反映されない原因です。

実は、MenuElementにstaticを付けなくてプログラム的に処理することも出来ます。
それは、
if( Key[ KEY_INPUT_DOWN ] == 1 ){ // 下キーが押された瞬間だけ処理
が関係します。考えてみてください。

ののき さんが書きました:だとすれば、私は
for( int i=0; i<5; i++ ){ // メニュー項目数である5個ループ処理
if( i == SelectNum ){ // 今処理しているのが、選択番号と同じ要素なら
MenuElement.x = 80; // 座標を80にする
}
else { // 今処理しているのが、選択番号以外なら
MenuElement.x = 100;// 座標を100にする
}
}
の部分で
MenuElement.x = 100;// 座標を100にする
が独立していると勘違いしていたことになり、この座標の変更が // メニュー項目要素を5つ作る の
値に反映することに気付いていなかったから、バグを修正することが今まで出来なかったということなのでしょうか。


これについては説明が良く分かりません。
何に対して独立しているという話でしょう?
ものすごくプログラムの流れという面で理解できていない感じを受ける内容なのですが。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ののき

Re: 選択画面の応用 バグ修正

#9

投稿記事 by ののき » 14年前

softya様の言うとおり、私はいまいちプログラミングの流れを理解できていないようですね。
独立している、という言葉は他の部分のコードと関連せずに、数値を書き換えているという意味で使いました。もっとも今から思い返してみると、数値を書き換えているという意味で使ったのに、書き換えられる側の処理に関連していないというのはおかしな話です。

また、softya様からの助言である
if( Key[ KEY_INPUT_DOWN ] == 1 ){ // 下キーが押された瞬間だけ処理
の部分もコードを深く理解するために、いろいろといじってみようと思います。

丁寧なご説明と問題点の指摘が初心者の私にとって大変参考になりました。改めてご指導いただきありがとうござました。

閉鎖

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