前提・実現したいこと
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で作成中
コードは判定を行っているところを載せていますが必要であれば全文載せたいと思います。
回答2件
あなたの回答
tips
プレビュー