【筆記】C++ 命令列小遊戲 節奏大師(別踩白塊) 的製作
一.遊戲介紹
計組實驗大作業要在板子上做一個小遊戲,我們組要做節奏大師,先在命令列裡做了一個,主介面只有4*20.
可以選歌,目前支援的有:1.兩隻老虎,
2.兩隻老虎無盡版。
開始遊戲後
號會從右邊不斷出現並往左邊移動,在到達最左端時變為
號,這個時候需要對應地按下數字1,2,3,4鍵來命中它們。
如果成功命中,就會播放一聲《兩隻老虎》裡的旋律,遊戲繼續。
如果按了錯誤的鍵或者沒有按鍵,遊戲就會結束。
遊戲有計分功能,擊中一個音樂塊算一分,還有記錄最高分的功能,而且可以通關(除了無盡模式)。
講真這應該大一而不是大三做
二.預備知識
做命令列小遊戲需要什麼。
1. 獲取輸入
做這種命令列小遊戲首先得知道怎麼獲得使用者輸入,這裡的獲取輸入顯然指的不是使用者輸入一段字元再打回車輸入,而是使用者的每個操作都得立即接收。
windows平臺 <conio.h>
int _kbhit() ,返回是否有按鍵在緩衝區中。
char _getch(),返回緩衝區中的第一個按鍵,如果緩衝區中沒有就一直等待第一個輸入並返回。
注意,getch前面有個下劃線,不帶下劃線的版本我的gcc無法編譯。
原理具體解釋很麻煩,拿這個小程式玩一會就懂了。
#include <conio.h>
#include <iostream>
int main()
{
while (1)
if(_kbhit()) //如果有按鍵按下,則_kbhit()函式返回真
printf("input %d\n", _getch() );
}
清空緩衝區:while(_kbhit()) _getch();
2.播放聲音
windows平臺 <windows.h>
void Beep(int,int),播放一個音符,第一個引數表示音調,第二個引數表示時長。
同樣不多解釋,下面的程式玩一下就懂了。mnote是預設音調(標準音7個,最後一個是一個低音,都可以隨便改),TwinTiger是兩隻老虎的譜子。
#include <stdio.h>
#include <windows.h>
#include <bits/stdc++.h>
using Song = std::vector<std::pair<int,int>>;
int main()
{
const int mnote[] = {0,440,495,550,587,660,733,825,325};
const Song TwinTiger =
{
{1,4},{2,4},{3,4},{1,4},
{1,4},{2,4},{3,4},{1,4},
{3,4},{4,4},{5,4},{0,4},
{3,4},{4,4},{5,4},{0,4},
{5,3},{6,1},{5,3},{4,1},{3,4},{1,4},
{5,3},{6,1},{5,3},{4,1},{3,4},{1,4},
{3,4},{8,4},{1,4},{0,4},
{3,4},{8,4},{1,4},{0,4},
};
for(auto note : TwinTiger)
{
Beep(mnote[note.first],note.second*100);
}
}
3.清屏與閃屏
呼叫system("cls")
清屏,這樣有個壞處是會閃屏。
因為我的程式最終要寫到板子上,就不去解決這個問題了。
想解決閃屏問題的同學可以搜尋與緩衝區有關的知識,用雙緩衝解決閃屏。
三.主要實現思路
首要目標是要實現音樂塊從右邊出現,逐個往左的效果。
1. 地圖
首先要有R*C尺寸的地圖,把 號和空格畫在上邊,再列印就可以把地圖顯示出來。列印文字同理。
2. 時間軸
記錄遊戲執行的時間,每過一個單位的時間就將所有的塊往左移動一格,每隔一段時間就在右邊新加入一個塊。
3. 每行儲存塊產生的時間
本質上說,每行的關鍵要素就在於它有幾個塊,分別在什麼位置。
但是位置是一個變數,不如儲存這些塊產生的時間,當需要列印地圖的時候,與當前執行時間相減就可以得到儲存位置。
4. 佇列儲存
按上面的方法,加入一個塊就是在這一行的隊尾加入一個元素:當前時間。
當最早的塊脫離地圖時,就將它出隊即可。
5. 簡化的方法
實際上不用顯式地去做出隊操作,每次從隊尾遍歷所有可以被顯示的塊,碰到一個不能顯示的直接停止遍歷,簡單,方便。
6. 可行的優化
可以使用迴圈佇列去降低空間複雜度,暫未實現。
7. 程式碼實現
/** 使用佇列記錄音樂塊 */
int block[R+1][T], lim[R+1];
void push(int r)
{
block[r][lim[r]++] = gameRunTime;
}
需要顯示在地圖上時
for(int i=lim[r]-1; i>=0 && block[r][i]-gameRunTime+C>=1; --i)
{
gameMap[r][block[r][i]-gameRunTime+C] = '*';
if(gameMap[r][1]!=' ')
gameMap[r][1]='#';
}
四.其它細節
想清楚上述問題後,其它細節逐個加入就行:
1. 整體流程
1 歡迎介面
2 選關,進入遊戲
3 塊逐個出現,並往左移動
4 當有塊在最左端時,檢測按鍵
5 如果按鍵正確,繼續遊戲
6 否則,顯示結束頁面
7 返回1
2. 選關功能
因為只有兩種模式,所以相當於按任意鍵切換到另一模式。
void chooseSongs(int now)
{
gameClass = now;
memset(gameMap,0,sizeof(gameMap));
prints(2,3,"Choose Songs:");
if(now==0)
prints(4,1,"← Twin Tigers →");
else
prints(4,1,"←TwinTigerEndless→");
while(_kbhit()) _getch();
if(_getch()==13)
return;
else
return chooseSongs(now^1);
}
3.檢測按鍵
/* 按鍵檢測 */
int checkFail()
{
while(_kbhit())
{
int row = _getch()-'0';
if(gameMap[row][1] != ' ')
{
playNote(score++);
gameMap[row][1] = ' ';
}
else
{
return 1;
}
}
for(int r=1;r<=R;++r)
{
if(gameMap[r][1]!=' ')
{
return 1;
}
}
return 0;
}
五.後記
原始碼和程式下載。
因為沒有太大的技術含量,就不掛在github上了。
放在csdn上,如果有需要可以去下載,只需要兩積分。
如果實在沒有積分或者C幣,可以留言,我看到了會發,但是不保證時間。
注意使用的時候一定要保持輸入法在英文狀態