質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

DXライブラリ

DXライブラリとは、DirectXを使ったWindowsソフトの開発に必ず付いて回るDirectXやWindows関連のプログラムを使い易くまとめた形で利用できるようにしたC++言語用のゲームライブラリです。

Q&A

解決済

2回答

2786閲覧

音ゲーで特定のキーを押すと1回だけ判定され、ノーツを削除する処理を実装したい

sinigami

総合スコア6

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

DXライブラリ

DXライブラリとは、DirectXを使ったWindowsソフトの開発に必ず付いて回るDirectXやWindows関連のプログラムを使い易くまとめた形で利用できるようにしたC++言語用のゲームライブラリです。

0グッド

0クリップ

投稿2021/02/15 01:31

編集2021/02/16 07:20

前提・実現したいこと

C++とdx.libを用いて音ゲーを制作しているのですが、特定のキー(例えばDFJK)を押したときに一回だけ押されたと判定され、タイミングが合っていたらノーツを削除する処理を実装したいです。現状のコードでは、ノーツが消えるところまでは実装できているのですが、キーを長押ししているとその間ずっと判定されていて、ノーツが消えてしまいます。キーを長押ししていても1回しか判定されない処理を実装したいので、アドバイスよろしくお願いいたします。

発生している問題・エラーメッセージ

キーを長押ししているとその間ノーツが削除されてしまう。

該当のソースコード

C++, dx.lib ---------------------------------------------- #include "DxLib.h" #define WIN_W 800 // ウインドウの横幅 #define WIN_H 600 // ウインドウの縦幅 #define MAX_READ 2000 // 読み込みの最大数 #define LANE_NUM 4 // レーンの数 #define NOTE_NUM 1000 // ノーツの最大数 #define NOTE_WIDTH 72 // ノーツの幅 #define NOTE_HEIGHT 20 // ノーツの高さ #define JUDGE_Y 500 // 判定ラインのY座標 static const int KEYS[LANE_NUM] = { KEY_INPUT_E, // Eキー取得 KEY_INPUT_F, // Fキー取得 KEY_INPUT_J, // Jキー取得 KEY_INPUT_I // Iキー取得 }; struct MUGIC_DATA { double bpm = 120; // BPMが指定されていなければ120に設定 double offset = 0; // ノーツが出現する時間が指定されていなければ char song_name[MAX_READ] = "shinigamitowaltz.mp3"; // 曲名 char music_file_path[MAX_READ]; // ファイルのパス名 double perfect_times[LANE_NUM][NOTE_NUM] = { 0 }; // 判定ラインにノーツが到達する時刻 int perfect_time_size[LANE_NUM] = { 0 }; // 初期化処理の0と入ってくる0とを区別する }; // #STARTより上の情報を読み込む bool loadHumenOptions(MUGIC_DATA* music_data, FILE* fp) { char str[MAX_READ], *next_token = NULL, tstr[MAX_READ]; while ((fgets(str, MAX_READ, fp)) != NULL) { strncpy_s(tstr, MAX_READ, str, 6); if (strcmp(tstr, "#START") == 0) { return true; } char* first = strtok_s(str, ":", &next_token); if (first == NULL) continue; if (strcmp(first, "BPM") == 0) // BPM読み込み { char* second = strtok_s(NULL, ":", &next_token); music_data->bpm = atof(second); } else if (strcmp(first, "OFFSET") == 0) // OFFSET読み込み { char* second = strtok_s(NULL, ":", &next_token); music_data->offset = atof(second); } else if (strcmp(first, "TITLE") == 0) // TITLE読み込み { char* second = strtok_s(NULL, "\n", &next_token); strcpy_s(music_data->song_name, second); } else if (strcmp(first, "WAVE") == 0) // WAVE読み込み { char* second = strtok_s(NULL, "\n", &next_token); strcpy_s(music_data->music_file_path, second); } } return false; } // 譜面データを読み込む void loadHumen(MUGIC_DATA* music_data, FILE* fp) { char line[MAX_READ], *next_token = NULL, delim[4] = ", \n"; for (int col = 0; col < LANE_NUM; col++) { music_data->perfect_time_size[col] = 0; } for (int row = 0; (fgets(line, MAX_READ, fp)) != NULL; row++) { char* str = strtok_s(line, delim, &next_token); if (str == NULL) continue; for (int col = 0; str != NULL; col++) { int length = strlen(str); for (int i = 0; i < length; i++) { if ('1' <= str[i] && str[i] <= '9') { music_data->perfect_times[col][music_data->perfect_time_size[col]++] = 60.0 * 4 * (row + (double)i / length) / music_data->bpm - music_data->offset; } } str = strtok_s(NULL, delim, &next_token); } } } // 譜面データファイルのopen/closeをする処理 bool loadHumenData(MUGIC_DATA* music_data, const char* file_name) { FILE* fp; if ((fopen_s(&fp, file_name, "r")) != 0 || fp == 0) { // "fpが0である可能性があります"というエラー対策 return false; // boolはC++ } bool loadable = loadHumenOptions(music_data, fp); if (loadable) loadHumen(music_data, fp); fclose(fp); return true; } // ノーツの構造体 struct NOTE { bool flag = false; float x = 0.0f; float y = 0.0f; }; // ノーツ初期化処理 void initNotes(int perfect_time_size[LANE_NUM], NOTE notes[LANE_NUM][NOTE_NUM]) { for (int col = 0; col < LANE_NUM; col++) { int lane_length = perfect_time_size[col]; for (int row = 0; row < lane_length; row++) { notes[col][row].flag = true; notes[col][row].x = 200.0f + 150.0f * col; } } } // ノーツ更新処理 void updateNotes(double current_time, double perfect_times[LANE_NUM][NOTE_NUM], NOTE notes[LANE_NUM][NOTE_NUM]) { // ノーツ座標更新 for (int col = 0; col < LANE_NUM; col++) { for (int row = 0; row < NOTE_NUM; row++) { if (notes[col][row].flag) notes[col][row].y = JUDGE_Y * (float)(current_time - perfect_times[col][row]) / 2 + JUDGE_Y; } } // 画面外かつ判定範囲外に出たノーツを削除 for (int col = 0; col < LANE_NUM; col++) { for (int row = 0; row < NOTE_NUM; row++) { if (notes[col][row].flag && WIN_H + NOTE_HEIGHT < notes[col][row].y && 0.3 < current_time - perfect_times[col][row]) { notes[col][row].flag = false; // ノーツを削除 } } } } // ノーツ判定処理 void judgeNotes(double current_time, double perfect_times[LANE_NUM][NOTE_NUM], NOTE notes[LANE_NUM][NOTE_NUM], char buf[256]) { // レーンに対応するキーが押されていて、かつ判定範囲内であればノーツを削除 for (int col = 0; col < LANE_NUM; col++) { for (int row = 0; row < NOTE_NUM; row++) { if (buf[KEYS[col]] == 1 // KEYS[4] = E,F,J,Iが押されている && notes[col][row].flag // ノーツが存在している && -0.03 < current_time - perfect_times[col][row] // -0.03...判定線より上の判定時間 && current_time - perfect_times[col][row] < 0.03) // 0.03...判定線より下の判定時間 { notes[col][row].flag = false; // ノーツを削除 } } } } // ノーツ描画処理 void drawNotes(NOTE notes[LANE_NUM][NOTE_NUM]) { for (int col = 0; col < LANE_NUM; col++) { for (int row = 0; row < NOTE_NUM; row++) { if (notes[col][row].flag) // ノーツが存在している { DrawBoxAA(notes[col][row].x, notes[col][row].y - NOTE_HEIGHT / 4, // / 2, notes[col][row].x + NOTE_WIDTH, notes[col][row].y + NOTE_HEIGHT / 4, // / 2, GetColor(255, 0, 0), // ノーツの色指定(Red,Green,Blue : 0~255) TRUE); //DrawCircleAA(notes[col][row].x, // 円の中心座標x // notes[col][row].y - NOTE_HEIGHT / 2, // 円の中心座標y // 20, // 円の半径(大きさ) // 20, // 円の角の数(多いほど滑らかな円になるが処理が重くなる) // GetColor(255, 0, 0), // ノーツの色指定(Red,Green,Blue : 0~255) // TRUE); // 円の内部まで塗りつぶすか(TRUEで塗りつぶし) } } } } // 処理を変更したい場合、WINAPIも変更する // 上で行った処理を呼び出す int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MUGIC_DATA music_data; NOTE notes[LANE_NUM][NOTE_NUM]; char buf[256]; //キー押下状態格納用配列 SetMainWindowText("KeyBorder"); //ウインドウのタイトルを設定 ChangeWindowMode(TRUE); //ウィンドウモードで起動 SetGraphMode(WIN_W, WIN_H, 32); //画面の解像度指定 SetWindowSizeChangeEnableFlag(FALSE); //画面サイズ変更不可 if (DxLib_Init() == -1) { return -1; } //DxLib初期化処理 SetDrawScreen(DX_SCREEN_BACK); //描画先を裏画面に設定 loadHumenData(&music_data, "test.txt"); // 譜面データ読み込み initNotes(music_data.perfect_time_size, notes); // ノーツ初期化処理 int bgmHandle = LoadSoundMem(music_data.music_file_path); PlaySoundMem(bgmHandle, DX_PLAYTYPE_BACK); LONGLONG start_count = GetNowHiPerformanceCount(); //while(裏画面を表画面に反映, メッセージ処理, 画面クリア) while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0) { GetHitKeyStateAll(buf); if (buf[KEY_INPUT_ESCAPE] == 1) // ESACAPEキーが押された時 { break; // 終了 } LONGLONG now_count = GetNowHiPerformanceCount(); double current_time = (now_count - start_count) / 1000000.0; // ノーツの更新処理 updateNotes(current_time, music_data.perfect_times, notes); // ノーツの判定処理 judgeNotes(current_time, music_data.perfect_times, notes, buf); // 判定線表示 DrawLine( 0, JUDGE_Y, WIN_W, JUDGE_Y, GetColor(0, 0, 255) // 判定線のRGB指定 ); // 境界線表示 // 1レーン目 DrawLine( 200, // 第一X(右)方向座標 X方向への境界線表示位置1 0, // 第一Y(上)方向座標 Y方向への境界線表示位置1 200, // 第二X(右)方向座標 X方向への境界線表示位置2 500, // 第二Y(上)方向座標 Y方向への境界線表示位置2 GetColor(0, 255, 0) // 境界線のRGB指定 ); DrawLine( 270, // 第一X(右)方向座標 X方向への境界線表示位置1 0, // 第一Y(上)方向座標 Y方向への境界線表示位置1 270, // 第二X(右)方向座標 X方向への境界線表示位置2 500, // 第二Y(上)方向座標 Y方向への境界線表示位置2 GetColor(0, 255, 0) // 境界線のRGB指定 ); // 2レーン目 DrawLine( 350, // 第一X(右)方向座標 X方向への境界線表示位置1 0, // 第一Y(上)方向座標 Y方向への境界線表示位置1 350, // 第二X(右)方向座標 X方向への境界線表示位置2 500, // 第二Y(上)方向座標 Y方向への境界線表示位置2 GetColor(0, 255, 0) // 境界線のRGB指定 ); DrawLine( 420, // 第一X(右)方向座標 X方向への境界線表示位置1 0, // 第一Y(上)方向座標 Y方向への境界線表示位置1 420, // 第二X(右)方向座標 X方向への境界線表示位置2 500, // 第二Y(上)方向座標 Y方向への境界線表示位置2 GetColor(0, 255, 0) // 境界線のRGB指定 ); // 3レーン目 DrawLine( 500, // 第一X(右)方向座標 X方向への境界線表示位置1 0, // 第一Y(上)方向座標 Y方向への境界線表示位置1 500, // 第二X(右)方向座標 X方向への境界線表示位置2 500, // 第二Y(上)方向座標 Y方向への境界線表示位置2 GetColor(0, 255, 0) // 境界線のRGB指定 ); DrawLine( 570, // 第一X(右)方向座標 X方向への境界線表示位置1 0, // 第一Y(上)方向座標 Y方向への境界線表示位置1 570, // 第二X(右)方向座標 X方向への境界線表示位置2 500, // 第二Y(上)方向座標 Y方向への境界線表示位置2 GetColor(0, 255, 0) // 境界線のRGB指定 ); // 4レーン目 DrawLine( 650, // 第一X(右)方向座標 X方向への境界線表示位置1 0, // 第一Y(上)方向座標 Y方向への境界線表示位置1 650, // 第二X(右)方向座標 X方向への境界線表示位置2 500, // 第二Y(上)方向座標 Y方向への境界線表示位置2 GetColor(0, 255, 0) // 境界線のRGB指定 ); DrawLine( 720, // 第一X(右)方向座標 X方向への境界線表示位置1 0, // 第一Y(上)方向座標 Y方向への境界線表示位置1 720, // 第二X(右)方向座標 X方向への境界線表示位置2 500, // 第二Y(上)方向座標 Y方向への境界線表示位置2 GetColor(0, 255, 0) // 境界線のRGB指定 ); DrawFormatString( 10, 10, GetColor(255, 255, 255), "t:%f\nbpm:%f\noffset:%f\n%s\n%s", current_time, music_data.bpm, music_data.offset, music_data.song_name, music_data.music_file_path ); // ノーツの表示 drawNotes(notes); } DxLib_End(); //DxLibの終了処理 return 0; //正常終了 }

試したこと

GetHitKeyStateAllなど、キーに関する処理を実装してみましたがどれもうまくいきませんでした。おそらくは処理の内容にミスがあったかと思いますが、どこがミスかわからないのでぜひお力をお借りしたいです。

補足情報(FW/ツールのバージョンなど)

VisualStudio2017で作成中
コードは判定を行っているところを載せていますが必要であれば全文載せたいと思います。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sinigami

2021/02/15 02:32

2週間程度は色々なワードで調べ、役に立ちそうなものはいくつか取り入れてみたのですが、長押しで判定されたままだったり、押しても反応しなくなったりしたので、自分の書き方ではうまくいかないと思い質問させていただきました。
退会済みユーザー

退会済みユーザー

2021/02/15 02:35 編集

自分のやり方で上手くいかないなら、既に上手く出来てるやり方を参考にすればいいと思うのですが、どのページのどのやり方を試して、どう上手く行かなかったのでしょう?
dodox86

2021/02/15 03:33

コードが断片的なのでこの質問を見た方々は判断できないのではないでしょうか。 buf[KEYS[col]]のbufは恐らくGetHitKeyStateAll()で取得したキー群の押下状況(フレームごとの押下判定回数)だったりするのかと思いますが、その更新の仕方にもよるでしょうし、double current_time などの値の取り方も謎です。
sinigami

2021/02/16 07:23 編集

色々調べた中で参考になりそうな記事がteratail内にあり、「1フレームに1度だけ動作させたい」という記事です。こちらの記事の「gpUpdateKey()」を参考に自分のコードに取り入れたのですが、入力が1回にはなりませんでした。「https://teratail.com/questions/88669」
sinigami

2021/02/16 07:19

一度コードの全文を載せたいと思います。
dodox86

2021/02/16 10:54

>@質問者 sinigamiさん DXライブラリにおけるGetHitKeyStateAll()を利用したキーのワンショット判定はteratailに限らず頻出の話題のようで、本日も出ました。 [dxlib キー入力で押した時と押されている間を実装したい。] https://teratail.com/questions/322787 既に回答いただいているYT0014さんの案の具現化の一例になるかと思います。
dodox86

2021/02/16 10:58

GetHitKeyStateAll()をラップした自作のgpUpdateKey()関数はそこら中でコピペられているようで、ググっても良くヒットしますね。
guest

回答2

0

GetHitKeyStateAll()で取得できるのは、取得時のキーの押下状態です。
sinigamiさんの欲しいのは、キーの押下状態に加えて、前回取得時からの変化状態です。

どこかに前回の押下状態を保持して、変化したか否かの判定を加えてください。

投稿2021/02/15 03:39

YT0014

総合スコア1750

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sinigami

2021/02/16 07:21

回答ありがとうございます。判定文ということですね、試してみます。
YT0014

2021/02/16 11:44

判定文とは限らず、主旨は、前回(前のフレームあたり?)の入力情報の保持が必要と言う事です。 ご提示いただいたjudgeNotes()を呼ぶ側で、bufの挙動を変更すれば、udgeNotes()の変更なしでも、ご希望の処理が可能です。
sinigami

2021/02/17 03:28

judgeNotes()を呼ぶ側というのは、WinMain内の // ノーツの判定処理 judgeNotes(current_time,   music_data.perfect_times,   notes, buf); のところですよね? bufの挙動の変更は()内のbufを変更するということでしょうか? それともbufの定義 char buf[256]; //キー押下状態格納用配列 を変更するということでしょうか?
sinigami

2021/02/17 03:53

すみません、他のサイトの情報を見ながらコードを描いたところ上手くいきました!質問に丁寧に答えてくださりありがとうございました!参考のサイトはこちら「https://qiita.com/legohasiri/items/ff258bb3cb4522b4aa66」 ノーツの判定処理を押された瞬間のみ処理するようにすれば良かったのですね。YT0014さんありがとうございました。とても参考になりました。
guest

0

自己解決

参考文献を見ながら解決出来ましたので、追加したコードを載せておきます。

c++,dx.lib

1static const int KEYS[LANE_NUM] = 2{ 3 KEY_INPUT_E, // Eキー取得 4 KEY_INPUT_F, // Fキー取得 5 KEY_INPUT_J, // Jキー取得 6 KEY_INPUT_I // Iキー取得 7}; 8 9int Key[256]; // キーが押されているフレーム数を格納する 10 11// キーの入力状態を更新する 12int gpUpdateKey() { 13 char tmpKey[256]; // 現在のキーの入力状態を格納する 14 GetHitKeyStateAll(tmpKey); // 全てのキーの入力状態を得る 15 for (int i = 0; i < 256; i++) 16 { 17 if (tmpKey[i] != 0) // i番のキーコードに対応するキーが押されていたら 18 { 19 Key[i]++; // 加算 20 } 21 else // 押されていなければ 22 { 23 Key[i] = 0; // 0にする 24 } 25 } 26 return 0; 27} 28 29------------------------------------------------------------------------------------------ 30// ノーツの更新処理 31updateNotes(current_time, 32 music_data.perfect_times, 33 notes); 34 35if (Key[KEY_INPUT_E] == 1) // Eキーが押された瞬間なら 36{ 37 // 押された瞬間の処理 38 // ノーツの判定処理 39 judgeNotes(current_time, 40 music_data.perfect_times, 41 notes, buf); 42} 43if (Key[KEY_INPUT_F] == 1) // Fキーが押された瞬間なら 44{ 45 // 押された瞬間の処理 46 // ノーツの判定処理 47 judgeNotes(current_time, 48 music_data.perfect_times, 49 notes, buf); 50} 51if (Key[KEY_INPUT_J] == 1) // Jキーが押された瞬間なら 52{ 53 // 押された瞬間の処理 54 // ノーツの判定処理 55 judgeNotes(current_time, 56 music_data.perfect_times, 57 notes, buf); 58} 59if (Key[KEY_INPUT_I] == 1) // Iキーが押された瞬間なら 60{ 61 // 押された瞬間の処理 62 // ノーツの判定処理 63 judgeNotes(current_time, 64 music_data.perfect_times, 65 notes, buf); 66}

投稿2021/02/17 04:19

sinigami

総合スコア6

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問