ホームへ戻る

 3.4章 簡単な選択画面を作る (マイナス方向へのループ)

 前章で簡単な選択画面を作りましたが、移動できる方向は下方向のみでした。

上への移動が、下方向への移動の考え方を逆にしただけでは実現出来ないため、2つの章に分けました。

どういうことかと言うと

  SelectNum = ( SelectNum + 1 ) % 5;

を計算することで、SelectNumに入る数値が 0→1→2→3→4→0→1→2→3→4→0(0→4のループ) とループさせることが出来ました。

それに対してこれを逆にしたいからと言って

  SelectNum = ( SelectNum - 1 ) % 5;

を計算することで、SelectNumに入る数値が 0→4→3→2→1→0→4→3→2→1→0(4→0のループ) にはなりません。

SelectNum がマイナスになってしまうと、ループは 0→-1→-2→-3→-4→0→-1→-2→-3→-4→0 になってしまいます。

期待する動作は、

下キーが押された時は、0→1→2→3→4→0→1→2→3→4→0 とループ(0→4のループ)し、

上キーが押された時は、0→4→3→2→1→0→4→3→2→1→0 とループ(4→0のループ)する振る舞いです。

これを実現するには、
SelectNum-1するのではなく、SelectNum+4するのです。

「?」と思う人もいるでしょうが、「割る数-1を足す」ことで見事に逆ループが計算出来るのです。

百聞は一見に如かず。 とりあえず計算してみましょう。

  SelectNum = ( SelectNum + 4 ) % 5;

において

SelectNumが0の時は、(0+4) % 5 となり、計算結果 SelectNum は
4になります。
SelectNumが4の時は、(4+4) % 5 となり、計算結果 SelectNum は
3になります。
SelectNumが3の時は、(3+4) % 5 となり、計算結果 SelectNum は
2になります。
SelectNumが2の時は、(2+4) % 5 となり、計算結果 SelectNum は
1になります。
SelectNumが1の時は、(1+4) % 5 となり、計算結果 SelectNum は
0になります。
SelectNumが0の時は、(0+4) % 5 となり、計算結果 SelectNum は
4になります。

どうでしょう。見事に逆向きのループが計算出来ました。

このように、プラス方向へのループは

  SelectNum = ( SelectNum + 1 ) % 5;

で計算出来

  SelectNum = ( SelectNum + 4 ) % 5;

で計算できることが分かりました。

これを踏まえて前章のメニュー項目の選択状態を上下に適用してみましょう。


#include "DxLib.h"

int Key[256]; // キーが押されているフレーム数を格納する

// キーの入力状態を更新する
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;
}

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

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
        ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定

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

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

                // 計算フェーズ 

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

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

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

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

		if( Key[ KEY_INPUT_DOWN ] == 1 || Key[ KEY_INPUT_UP ] == 1 ){ // 下キーか、上キーが押された瞬間
			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 );
		}

	}

	DxLib_End(); // DXライブラリ終了処理
	return 0;
}

実行結果


このように「割った余り」という考え方はゲームプログラムで重宝するので是非覚えておいて下さい。

→分からないことがあれば掲示板で質問して下さい


- Remical Soft -