1. 程式人生 > >【筆記】C++ 命令列小遊戲 節奏大師(別踩白塊) 的製作

【筆記】C++ 命令列小遊戲 節奏大師(別踩白塊) 的製作

一.遊戲介紹

計組實驗大作業要在板子上做一個小遊戲,我們組要做節奏大師,先在命令列裡做了一個,主介面只有4*20.
在這裡插入圖asd述
可以選歌,目前支援的有: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幣,可以留言,我看到了會發,但是不保證時間。
注意使用的時候一定要保持輸入法在英文狀態