dポイントプレゼントキャンペーン実施中!

つい一ヶ月程前に勉強を始めたプログラム初心者です。
現在、ゲームプログラミングについて学んでいるのですが、メニュー画面のカーソル移動処理で少し困っています。
キー入力に応じてカーソルを移動させる事は出来るのですが、一回のキー入力でカーソルが一気に動いてしまい、制御が出来ない状態になっています。
※一度下キーを押すと一気に次の処理まで行われてカーソルが荒ぶるような状態です。
キー入力後の処理にワンクッション置く事が出来れば解決するんでしょうけど、どのようにすればいいのかわかりません・・・。
自分でも調べてみたのですが、調べ方が悪いのか自分の求めている答えが出てきませんでした。

文章だけではわからないかと思いますのでプログラムを載せておきます(練習用に組んだものなのでファイル分けはしていません)。
自力で修正しつつのプログラムをそのまま貼り付けたのでカーソルが一番下にある時の処理は消えたままになっていますが、上にあるものと同じ事を書いていました。

#include "DxLib.h"
~中略~
int se_cursor; // カーソル移動時用SE
int se_enter; // 決定時SE
int Green = GetColor( 0, 255, 0 );
int White = GetColor( 255, 255, 255 );
int Cursor = 0;
int CursorX = 190; //カーソルのX座標
int CursorY = 300; //カーソルのY座標
se_cursor = LoadSoundMem("SE/se_maoudamashii_system35.wav"); // SEフォルダからSEをロード
se_enter = LoadSoundMem("SE/nc23432.wav");

enum{
TITLE,
GAME,
RESULT
}status=TITLE;

char key[256];

while(Process(key)){
switch(status){
case TITLE:

SetFontSize( 40 );
SetFontThickness( 8 );
ChangeFont( "HGS創英角ポップ体");
ChangeFontType( DX_FONTTYPE_ANTIALIASING_EDGE );

DrawFormatString( 250, 150, Green, "テスト"); // 文字を描画する

SetFontSize( 30 );
SetFontThickness( 1 );
ChangeFont( "ぷちくまふぉんと");

DrawFormatString( 220, 300, White, "はじめから");
DrawFormatString( 220, 340, White, "つづきから");
DrawFormatString( 250, 380, White, "おわる");
DrawFormatString( CursorX, CursorY, White, "→");

if(CursorY == 300){
if(key[KEY_INPUT_Z]==1){
PlaySoundMem( se_enter, DX_PLAYTYPE_BACK );
status=GAME;
}else if(key[KEY_INPUT_DOWN]==1){
PlaySoundMem( se_cursor, DX_PLAYTYPE_BACK ); //SEを再生する
CursorY=340;
}else if(key[KEY_INPUT_UP]==1){
PlaySoundMem( se_cursor, DX_PLAYTYPE_BACK ); //SEを再生する
CursorY=380;
}
}
if(CursorY == 340){
if(key[KEY_INPUT_Z]==1){
PlaySoundMem( se_enter, DX_PLAYTYPE_BACK );
status=GAME;
}else if(key[KEY_INPUT_DOWN]==1){
PlaySoundMem( se_cursor, DX_PLAYTYPE_BACK );
CursorY=380;
}else if(key[KEY_INPUT_UP]==1){
PlaySoundMem( se_cursor, DX_PLAYTYPE_BACK );
CursorY=300;
}
}

break;
case GAME:
SetFontSize( 50 ); // フォントサイズ
SetFontThickness( 8 ); // 太さ
ChangeFont( "HGS創英角ポップ体"); // フォント名
ChangeFontType( DX_FONTTYPE_ANTIALIASING_EDGE );
DrawFormatString( 0, 18, Green, "Xキーを押すとシーン移行します。"); // 文字を描画する
if(key[KEY_INPUT_X]==1){
status=RESULT;
}
break;

case RESULT:
DrawFormatString( 0, 18, Green, "Cキーを押すとシーン移行します。");
if(key[KEY_INPUT_C]==1){
status=TITLE; //テスト用なのでTITLEに戻してループさせる
}

break;
}
}

SetDrawScreen( DX_SCREEN_BACK ) ;

~以下省略~

長くなって申し訳ありませんが宜しくお願い致します。

「C++ DirectX カーソルの移動処」の質問画像

質問者からの補足コメント

  • 解答ありがとうございます、要するにキーを押して座標が変動した瞬間に次の処理を行おうとしているので、if(CursorY == 300){
    }else if(CursorY == 340{
    }
    の処理を挟む事で一度のキー押下で次の処理を行わないようにするという解釈で良いのですかね?
    少しわかりづらいかもしれません、説明下手ですみません・・・。

    No.1の回答に寄せられた補足コメントです。 補足日時:2015/07/20 18:40
  • ~追記~
    申し訳ありません、少し解釈が間違っていたようです。
    if(CursorY == 300){
    }else if(CursorY == 340{
    }
    カーソル移動処理を個別に分けるのでは無く、この間に全ての処理を入れるという事でよろしいでしょうか?
    何度も申し訳ありません・・・。

      補足日時:2015/07/20 18:46

A 回答 (4件)

ああ、ちなみにPCのキーボードで発生するかは不明ですが、


チャタリングという現象もある機能性がありますので、適度なSleep()は必要かと思われます。

私がゲームのスクリプトで作っていた時には
「キーを押して、離した時に反応」するようにしていましたけどね。
(key[KEY_INPUT_Z]==0 && oldkey[KEY_INPUT_Z]==1)
のような条件式に。

マウスのボタンだと「押して離した時に反応」でいいのですが、キーボードだと「押された時(キーリピートの為?)」というのが、Windowsで一般的なインターフェースっつぽいですけどね。
(ダイアログのボタンなどにフォーカスがあった時の動作…とかね。)
    • good
    • 0
この回答へのお礼

何度も解答ありがとうございます。
なるほど、キーを離した時に反応ですか、それなら確かに今までのようにカーソルが荒ぶるという事は無くなりそうですね。
現在多忙のためにまだ試してはいませんが・・・。

一つ前の解答のプログラムも見せて頂きましたが、わかりやすく注釈まで入れて頂いて嬉しい限りです。
これからも頑張って勉強していきます、解答ありがとうございました。

お礼日時:2015/07/22 10:57

>現状ではこのようなプログラムになっているのですが、Sleep以外で遅延を発生させる手段はあるのですかね?


>何度も質問申し訳ありません・・・。

char key[256];
が何のための配列なのか?
というのが不明ですが、
キーが押下されていると1が設定される。というものであれば、
「押して」「離した」というのをチェックするようにすればいいでしょう。
配列2つ用意して、ループの最後で「前回の値を保持」して、片方が離された状態、もう片方が押された状態という時にメニュー項目の移動を処理する。とかですね。
# キーリピートでどういう動作するか確認は必要かと思われますけども。

char oldkey[256];
を用意して……

while(Process(key)){
 switch(status) {
  case TITLE:
    :
   if(key[KEY_INPUT_Z]==1 && oldkey[KEY_INPUT_Z]==0) {
    PlaySoundMem( se_enter, DX_PLAYTYPE_BACK );
    switch(CursorY) {
     case 300:status=GAME;break;
     case 340:status=GAME;break; // 続きからのステータスに
     case 380:status=GAME;break; // 終わるのステータスに
    }
    // メニュー決定なので、必要であればループから抜けるように制御する
   } else if(key[KEY_INPUT_DOWN]==1 && oldkey[KEY_INPUT_DOWN]==0) {
    PlaySoundMem( se_cursor, DX_PLAYTYPE_BACK );
    CursorY = ((((CursorY - 300) / 40) + 1) % 3) * 40 + 300;
    // CursorYから300引くと0・40・80のいずれかの値になり、それを40で割ると0から2になる。
    // +1してから3の剰余算で1つ繰り上がった値になる。(0なら1、1なら2、2なら0)
    // その結果に40を掛けて、最初に引いた300を加える事で
    // 300→340→380→300とループするようになる
   } else if(key[KEY_INPUT_UP]==1 && oldkey[KEY_INPUT_UP]==0) {
    PlaySoundMem( se_cursor, DX_PLAYTYPE_BACK );
    CursorY = ((((CursorY - 300) / 40) + 2) % 3) * 40 + 300;
    // 上記と似たような計算。(+2してから3で剰余算する段階でどういう値の遷移するか考えてみてくださいな)
    // 380→340→300→380とループする
    // ちなみに、CursorYの値を直接変更するのではなく他の変数でメニュー項目の番号を0~2で持ち、
    // カーソル表示の際に座標演算を行う。という方法を私なら採りますね。
   }
   break;
  case GAME:
   :
 }
 memcpy(oldkey, key, sizeof(key));
}

こんな感じですかね。
GetKeyboardState()がBYTE配列で256個の要素を必要とする……ようですが、
こちらはキーの押下状態は最上位ビットのようですしねぇ……。
    • good
    • 0

なんかいろいろと混乱されているようですが…


ステップ実行で追いかけてみてください。
その上で、意図した動作をしているか確認してください。

プログラムコードは書かれたとおりにしか動作しませんし。
    • good
    • 0
この回答へのお礼

解決・・・とは少し違いますが先日頂いた解答を参考に一度プログラムを組み、間にSleep(90)の記述をする事によりカーソルが荒ぶる事は無くなりました。
現状ではこのようなプログラムになっているのですが、Sleep以外で遅延を発生させる手段はあるのですかね?
何度も質問申し訳ありません・・・。

if(key[KEY_INPUT_ESCAPE]==1){
status=END;
}
if(CursorY == 300){
if(key[KEY_INPUT_Z]==1){
PlaySoundMem( se_enter, DX_PLAYTYPE_BACK );
status=GAME;
}else if(key[KEY_INPUT_DOWN]==1){
PlaySoundMem( se_cursor, DX_PLAYTYPE_BACK ); //SEを再生する
Cursory=340;
Sleep(90);
}else if(key[KEY_INPUT_UP]==1){
PlaySoundMem( se_cursor, DX_PLAYTYPE_BACK ); //SEを再生する
CursorY=380;
Sleep(90);
}
}else if(CursorY == 340){
if(key[KEY_INPUT_Z]==1){
PlaySoundMem( se_enter, DX_PLAYTYPE_BACK );
status=GAME;
}else if(key[KEY_INPUT_DOWN]==1){
PlaySoundMem( se_cursor, DX_PLAYTYPE_BACK );
CursorY=380;
Sleep(90);
}else if(key[KEY_INPUT_UP]==1){
PlaySoundMem( se_cursor, DX_PLAYTYPE_BACK );
CursorY=300;
Sleep(90);
}
}

お礼日時:2015/07/21 07:19

細かく追っていませんが…



>if(CursorY == 300){
> :
>}
の中でCursorYが340になったあと、
次の条件判定である、
>if(CursorY == 340){
を、素通りできるでしょうか?


if(CursorY == 300){
}
if(CursorY == 340){
}

if(CursorY == 300){
} eles if(CursorY == 340){
}
の違いは判りますか?
この回答への補足あり
    • good
    • 0
この回答へのお礼

解答ありがとうございます、要するにキーを押して座標が変動した瞬間に次の処理を行おうとしているので、if(CursorY == 300){
}else if(CursorY == 340){
}
の処理を挟む事で一度のキー押下で次の処理を行わないようにするという解釈で良いのですかね?
少しわかりづらいかもしれません、説明下手ですみません・・・。

お礼日時:2015/07/20 18:42

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!