【笔记】C++ 命令行小游戏 节奏大师(别踩白块) 的制作

版权声明:欢迎评论交流,转载请注明原作者。 https://blog.csdn.net/m0_37809890/article/details/84602634

一.游戏介绍

计组实验大作业要在板子上做一个小游戏,我们组要做节奏大师,先在命令行里做了一个,主界面只有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币,可以留言,我看到了会发,但是不保证时间。
注意使用的时候一定要保持输入法在英文状态

猜你喜欢

转载自blog.csdn.net/m0_37809890/article/details/84602634