**
带背景音乐的贪吃蛇小游戏
一、游戏主要的功能以及设计思路的简介
最近学了点C语言的编程,然后用了三天的时间写了一个贪吃蛇的小游戏, 我把我调试的程序的过程写一下附带程序的代码。
我用了三个多小时把贪吃蛇的基本的框架给写了出来,也就是说能够实现最简单的蛇吃食物,以及能够让蛇移动。
也就是说能够实现最简单的蛇吃食物,以及能够让蛇移动。然后我再此基础上想多加一些功能,例如加入一个背景音乐,增加游戏的关卡,让食物的种类的多样性,然后吃不同的食物会有相应的分数等等一些列的功能。
1.首先增加背景音乐,我使用了MFC中的PlaySound()这个函数。这个函数的使用很简单,百度一下就知道怎么用了。但是得加一些头文件:#include<windows.h>, #include<stdlib.h>, #pragma comment(lib,“winmm.lib”) 而且顺序不能反。但这个播放的音乐的格式只能是.wav格式的(这个确实是有点坑)。我把音乐的目录放到了PlaySound()这个函数中之后,运行的时候就听到哗哗的响声,根本听不到音乐的声音。这然我非常的郁闷和困惑。听到哗哗的响声说明程序确实打开了音乐,但不知道为什么会出现这样的声音,找度娘也没有找到。后来猜想可能是下载的.wav格式的音乐被破坏了,于是通过酷狗将.mp3.格式原声的音乐转化为.wav的格式的。加到程序中,最后程序顺利打开了音乐。说明我猜想的是对的------播放的必须是.wav格式的,而且要保证音乐是原声的,高品质的都不行。这样第一个功能实现了。费了我好一顿功夫。
2.增加游戏的关卡:这个实现很简单,我是用for语句做了一个关卡的循环,越高级的关卡的蛇的移动速度会加快。 蛇的移动的速度我用的Sleep()这个延迟函数实现的。也就是说越高级的关卡所设定的延迟的时间缩短就可以了。
3.给界面增加颜色:这部分我用color()函数实现的。也是MFC的的函数。这个我在程序中都有标明。
4.增加游戏者的命数:一开始我是将它和游戏的关卡放到了同一个for循环中,后来发现了一个bug。假设for循环循环三次,也就代表命数和关卡都有三次。但是若放到同一循环中则会出现问题(也就是会导致生命的次数与关卡的次数之和为三),后来我设了两个全局变量,通过全局变量的值来改变循环的次数。这样就把生命的次数与关卡的次数给分开了。
5.食物的种类:一开始我想输入一些特殊的符号来代替食物,比如爱心,雪花等等。但是输入进去之后命令行中显示问号,百度才知道编译器不识别这些特殊的符号,后来没办法只能输入一些常规的符号,比如键盘上的一些符号,还有圆形,矩形符号等。增加食物的种类还是比较简单的,方法也有很多种,如使用rand()函数随机分配,或者用if…else if …else 语句进行划分也是可以的,我用的是后者。
6.分数的计算:我这个程序实现的功能是它能记录每一关的分数,当你闯关之后会把最后的总分数给输出出来,期间如果浪费的一条生命,则这个关卡的分数就将清零,但保存上一关卡的分数。看似简单,但这个是我最难实现出来的。期间出现了很多的bug.因为每一关卡都有两种可能,要么是浪费一条生命,要么是闯关,就是因为这种随机性,所以每种情况都得考虑进去。比如闯过了第一关,但在第二关的时候浪费了两条生命,接着连闯两关。这种情况,按着我所说的程序设计的思路就是:先保存第一关的分数,然后死了两次之后,只能将这两次在这个关卡的分数清零。第一关卡的分数要保存,然后在连着创两关,将这两关的分数也要分别保存下来,最后求出总的成绩。刚才说的只是其中的一种,情况很多,我就不一一举例,总而言之要将这些情况都要考虑进去才行。
7.方向键的设计:我用了两组方向键控制小蛇,一种是w s a d ,另一种是用↑ ↓ ← →来进行控制,w s a d 控制比较好写,但↑ ↓ ← →就没有那么容易了,因为 w s a d是一个字符。使用getchar(),接受就可以了,但↑ ↓ ← →每一个是两个字符,所以得输入两次getchar(),,但如果两者都用的话就会出现冲突。后来我用了if语句先接收↑ ↓ ← →其中的一个字符,然后在这个if语句中在嵌套一个if语句来接受另外一个字符。最后在里面的if语句中使用分支语句,使其能够和之前的w s a d 的分支语句分配到一起去。总之是很麻烦的,如果两者只要一种,我推荐用w s a d,代码会简单点。
8.再说说bug:其实在编写的过程中,我进行了无数次的调试,因为bug真的是太多了,当然了学会调试也是程序员的基本功课吗,所以只要耐心一点,慢慢的调,总会出来的。嘿嘿。其中有一个bug是最头疼的。那就是按键只能接受一个字符,也就是说你按了一次方向键,小蛇就会按着你的方向前进,但当你手搓,连着按了两次,那么下次的方向就会按着你第二次的方向前进。你们读到这样会感到疑惑,这不是很正常吗,在游戏的中途确实是很正常的,但当快要结束的时候就不正常了,比如小蛇吃个这个食物正好闯关了,但你手搓连着按了两下,那么下一关的方向就会跟随你的第二次方向前进。但下一关的界面还没进去,蛇的方向就先确定了,这不是很可笑吗,比如举一个例子,假如第二关中蛇的初始方向是往左走,而你在第一关的时候最后按了一个向右的键,这个在第二关开始的时候蛇就会吃自己了,那就相当于第二关刚开始,你就直接挂掉了(白白浪费了一条生命),后来为了解决这个问题,我就在第一关快要结束的时候。设置了一个提醒,让在结束的时候别手欠瞎按。好了文字叙述就说到这样吧,咱么直接上代码。
**
二.游戏的界面的展示
1.游戏的介绍界面
2游戏的选择界面
3.游戏的运行界面
这个图片的姓名是我瞎打的
三.程序代码
// An highlighted block
//代码部分
/*
1.数据设计
2.模块化设计
3.实现过程
*/
#include<fstream>
#include<windows.h>//光标设置API
#include<stdio.h>
#include<stdlib.h>
#include<time.h>//食物随机
#include<conio.h>//按键监控
#include<iostream>
#include<mmsystem.h>
#include<iostream>
#include<string>
#pragma comment(lib,"WINMM.LIB")
using namespace std;
//辅助的宏定义
#define MAPHEIGHT 30//窗口的属性
#define MAPWIDTH 60
#define SNAKESIZE 40//蛇的最大的节数
#define SUM 4
int i = 0;
int j = 0;
int v = 0;
int m = 0;
int r = 0;
int q = 0;
int w = 0;
int u = 1;
int k= 0;
string z;
void color(int b);
//数据设计
//食物的结构体
struct food
{
//如何去定位:坐标
int x;
int y;
int score[3];
}food;
//蛇的结构体
struct
{
int x[SNAKESIZE];
int y[SNAKESIZE];
int len;//蛇的长度
int speed;//蛇的
}snake;
//全局变量
int key = 'w';//初始化移动方向
int changeFlag = 0;//蛇的变化的标记
void gotoxy(int x, int y);
//模块化设计->功能的划分->抽象的函数
//怎么抽象,具体了解业务逻辑
///
//1.画地图
void drawMap()
{
srand((unsigned int)time(NULL));//随机函数的种子
//1.圈地
//1.1左右边框
for(int i = 0; i <= MAPHEIGHT;i++)
{
if (i == 2)
{
for (int b = 0; b < MAPWIDTH; b += 2)
{
gotoxy(b, i);
color(6);
printf("■");
}
}
gotoxy(0, i);
printf("■");
gotoxy(MAPWIDTH, i);
printf("■");
color(6);
}
//1.2上下的边框
for (int i = 0; i <= MAPWIDTH; i+=2)
{
if (i == 20)
{
gotoxy(i, 1);
color(6);
printf("■");
}
if (i == 40)
{
gotoxy(i, 1);
color(6);
printf("■");
}
gotoxy(i, 0);
printf("■");
gotoxy( i,MAPHEIGHT);
printf("■");
color(6);
}
//2.画蛇
//2.1确定蛇的属性
snake.speed = 500;
//蛇头在屏幕的中间
if (j == 0)
{
snake.x[0] = MAPWIDTH / 2;
snake.y[0] = MAPHEIGHT / 2;
}
if (j == 1)
{
snake.x[0] = 20;
snake.y[0] = 25;
}
if (j == 2)
{
snake.x[0] = 10;
snake.y[0] = 25;
}
//2.2画蛇头
color(4);
gotoxy(snake.x[0], snake.y[0]);
printf("■");
//2.3画剩下的身体
for (int k = 1; k < snake.len; k++)
{
snake.x[k] = snake.x[k - 1] + 2;
snake.y[k] = snake.y[k - 1];
gotoxy(snake.x[k], snake.y[k]);
printf("■");
}
//3.画食物
//3.1 确定坐标
while (1)
{
food.x = rand() % (MAPWIDTH - 4) + 2;
food.y = rand() % (MAPHEIGHT - 4) + 3;
for (int k = 0; k < snake.len; k++)
{
if (snake.x[k] == food.x&&snake.y[k] == food.y)
{
u = 0;
break;
}
}
if (u&&(food.x % 2 == 0))
{
break;
}
}
//3.2 画出食物
gotoxy(food.x,food.y);
color(7);
printf("●");
}
//2.食物的产生
void createFood()
{
//蛇把食物吃了
if (snake.x[0] == food.x&&snake.y[0] == food.y)
{
srand(unsigned int(time(NULL)));
//产生的食物不能在蛇的身上,并且坐标要是偶数
while (1)
{
int flag = 1;
food.x = rand() % (MAPWIDTH - 4) + 2;
food.y = rand() % (MAPHEIGHT - 4) + 3;
//产生的食物不能在蛇的身上
for (int k = 0; k < snake.len; k++)
{
if (snake.x[k]== food.x&&snake.y[k] == food.y)
{
flag = 0;
break;
}
}
if (flag&&food.x % 2 == 0)
{
break;
}
}
color(13);
gotoxy(24, 1);
cout << "姓名:"<<z << endl;
color(13);
gotoxy(44, 1);
cout << "第" << j+1 << "关" << endl;
gotoxy(food.x, food.y);
color(7);
if (i % 4 == 0)
{
if (j == 0)
{
food.score[j] = food.score[j] + 100;//统计每一局的分数
}
if (j == 1)
{
food.score[j] = food.score[j] + r + 110;
r = 0;
}
if (j == 2)
{
food.score[j] = food.score[j] + q + r + 120;
r = 0;
q = 0;
}
printf("●");
}
else if (i % 5 == 0)
{
if (j == 0)
{
food.score[j] = food.score[j] + 100 * 3;//统计每一局的分数
}
if (j == 1)
{
food.score[j] = food.score[j] + r + 100 * 4;
r = 0;
}
if (j == 2)
{
food.score[j] = food.score[j] + q + r + 100 * 5;
r = 0;
q = 0;
}
printf("$");
}
else if (i % 7 == 0)
{
if (j == 0)
{
food.score[j] = food.score[j] + 100 * 2;//统计每一局的分数
}
if (j == 1)
{
food.score[j] = food.score[j] + r + 100 * 3;
r = 0;
}
if (j == 2)
{
food.score[j] = food.score[j] + q + r + 100 * 4;
r = 0;
q = 0;
}
printf("@");
}
else
{
if (j == 0)
{
food.score[j] = food.score[j] + 90;//统计每一局的分数
}
if (j == 1)
{
food.score[j] = food.score[j] + r + 100;
r = 0;
}
if (j == 2)
{
food.score[j] = food.score[j] + q + r + 110;
r = 0;
q = 0;
}
printf("*");
}
color(13);
gotoxy(2, 1);
cout << "得分" << food.score[j] << endl;
snake.len++;
changeFlag = 1;//蛇的标记是1
i++;
}
}
//3.按键的操作
void keyDown()
{
//无按键的处理
if (_kbhit())
{ //有按键接收
fflush(stdin);
key = _getch();
if (key == 0xE0)
{
key = _getch();
if (key == 72)
{
key = 'w';
}
if (key == 80)
{
key = 's';
}
if (key == 75)
{
key = 'a';
}
if (key == 77)
{
key = 'd';
}
}
}
//擦除最后的位置
if (!changeFlag)
{
color(4);
gotoxy(snake.x[snake.len - 1], snake.y[snake.len - 1]);
printf(" ");
color(6);
gotoxy(0, 0);
printf("■");
}
//后面的蛇身
for (int i = snake.len - 1; i > 0; i--)
{
color(4);
snake.x[i] = snake.x[i - 1];
snake.y[i] = snake.y[i - 1];
}
//移动方向的处理:上下左右
switch (key)
{
{
case 'w':
case 'W':
snake.y[0]--;
break;
}
{
case 's':
case 'S':
snake.y[0]++;
break;
}
{case 'a':
case 'A':
snake.x[0] -= 2;
break;
}
{case 'D':
case 'd':
snake.x[0] += 2;
break;
}
}
gotoxy(snake.x[0], snake.y[0]);
printf("■");
changeFlag = 0;
gotoxy(MAPHEIGHT+2, 0);//移动不能一直看着光标
}
//4.蛇的状态:判断是否结束游戏
int snakeStatus()
{
if (snake.x[0] == 0 || snake.x[0] == MAPWIDTH || snake.y[0] == 2|| snake.y[0] == MAPHEIGHT )
return 0;
//蛇头不能装自己
for (int k = 1; k < snake.len; k++)
{
if (snake.x[0] == snake.x[k] && snake.y[k] == snake.y[0])
return 0;
}
return 1;
}
//5.辅助函数:光标移动
void gotoxy(int x, int y)//要自己实现
{
//调用win32 API去设置控制的光标的位置
//1.找到控制台的这个窗口
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
//2.光标的结构体
COORD coord;
//3.设置坐标
coord.X = x;
coord.Y = y;
//同步到控制台
SetConsoleCursorPosition(handle, coord);//设置控制台光标的位置
}
//颜色的设置
void color(int b)
{
HANDLE hConsole= GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hConsole, b);
}
void menu();
int main()
{
//PlaySound(TEXT("SystemStart"), NULL, SND_ALIAS | SND_ASYNC);
//mciSendString("open E:\\KuGou\\wav alias wav", NULL, 0, NULL);
//mciSendString("play wav repeat", NULL, 0, NULL);
//_getch();
gotoxy(10, 3);
color(13);
cout << "************************贪吃蛇小游戏******************************" << endl;
cout << endl;
cout << endl;
cout << endl;
color(14);
cout << " /游戏介绍//" << endl;
cout << endl;
cout << " 1.游戏有三关" << endl;
cout << endl;
cout << " 2.每个人有三条命" << endl;
cout << endl;
cout << " 3.吃不同的食物会得到相应的分数" << endl;
cout << endl;
cout << " 4.↑ ↓ ← → 或者w(W) s(S) a(A) d(D) 控制方向" << endl;
cout << endl;
cout << " 5.游戏可以选择相应的背景音乐" << endl;
cout << endl;
cout << " 6.还在等什么,赶快开始吧" << endl;
cin.get();
system("cls");
gotoxy(10, 3);
color(13);
cout << "**************************贪吃蛇小游戏******************************" << endl;
int c;
color(11);
gotoxy(15, 5);
cout << "1.请输入姓名" << endl;
gotoxy(15, 6);
getline(cin, z);
color(11);
gotoxy(30,5);
cout << "2.要选择的背景音乐" << endl;
color(11);
gotoxy(30,6);
cout << "(1):远走高飞" << endl;
color(11);
gotoxy(30,7);
cout << "(2).出山" << endl;
color(11);
gotoxy(30,8);
cout << "(3).盗将行" << endl;
int g=0;
gotoxy(30,9);
scanf("%d/n", &g);
if (g == 1)
{
PlaySound(LPCSTR("C:\\Kugou\\Convert\\金志文、徐佳莹 - 远走高飞 (合唱版)"), NULL, SND_FILENAME | SND_ASYNC);
}
if (g == 2)
{
PlaySound(LPCSTR("C:\\Kugou\\Convert\\仇志 - 出山"), NULL, SND_FILENAME | SND_ASYNC);
}
if (g == 3)
{
PlaySound(LPCSTR("C:\\Kugou\\Convert\\邱大鑫 - 盗将行"), NULL, SND_FILENAME | SND_ASYNC);
}
menu();
gotoxy(50, 6);
scanf("%d/n", &c);
switch (c)
{
case 3:
system("cls");
for (int t = 0; t < 3; )
{
Sleep(snake.speed);
drawMap();
while (i <= SUM)
{
if (m == 0)
{
if (k == 0)
{
snake.len = 3;
}
if (i == SUM)
{
gotoxy(63,5);
color(14);
cout << "还" << endl;
gotoxy(63, 6);
color(14);
cout << "差" << endl;
gotoxy(63, 7);
color(14);
cout << "最" << endl;
gotoxy(63, 8);
color(14);
cout << "后 " << endl;
gotoxy(63, 9);
color(14);
cout << "一 " << endl;
gotoxy(63, 10);
color(14);
cout << "个" << endl;
gotoxy(63, 11);
color(14);
cout << "就 " << endl;
gotoxy(63, 12);
color(14);
cout << "闯 " << endl;
gotoxy(63, 13);
color(14);
cout << "关" << endl;
gotoxy(63, 14);
color(14);
cout << "了" << endl;
gotoxy(63, 15);
color(14);
cout << "*^*" << endl;
}
createFood();
Sleep(snake.speed);
keyDown();
if (!snakeStatus())
{
break;
}
k++;
}
if (m == 1)
{
if (i == SUM )
{
gotoxy(63, 5);
color(14);
cout << "还" << endl;
gotoxy(63, 6);
color(14);
cout << "差" << endl;
gotoxy(63, 7);
color(14);
cout << "最" << endl;
gotoxy(63, 8);
color(14);
cout << "后 " << endl;
gotoxy(63, 9);
color(14);
cout << "一 " << endl;
gotoxy(63, 10);
color(14);
cout << "个" << endl;
gotoxy(63, 11);
color(14);
cout << "就 " << endl;
gotoxy(63, 12);
color(14);
cout << "闯 " << endl;
gotoxy(63, 13);
color(14);
cout << "关" << endl;
gotoxy(63, 14);
color(14);
cout << "了" << endl;
gotoxy(63, 15);
color(14);
cout << "*^*" << endl;
}
createFood();
Sleep(snake.speed-200);
keyDown();
if (!snakeStatus())
{
break;
}
}
if (m == 2)
{
if (i == SUM )
{
gotoxy(63, 5);
color(14);
cout << "还" << endl;
gotoxy(63, 6);
color(14);
cout << "差" << endl;
gotoxy(63, 7);
color(14);
cout << "最" << endl;
gotoxy(63, 8);
color(14);
cout << "后 " << endl;
gotoxy(63, 9);
color(14);
cout << "一 " << endl;
gotoxy(63, 10);
color(14);
cout << "个" << endl;
gotoxy(63, 11);
color(14);
cout << "就 " << endl;
gotoxy(63, 12);
color(14);
cout << "闯 " << endl;
gotoxy(63, 13);
color(14);
cout << "关" << endl;
gotoxy(63, 14);
color(14);
cout << "了" << endl;
gotoxy(63, 15);
color(14);
cout << "*^*" << endl;
}
createFood();
Sleep(snake.speed-300);
keyDown();
if (!snakeStatus())
{
break;
}
}
}
if (i > SUM)
{
system("cls");
gotoxy(10, 3);
color(13);
cout << "**************************贪吃蛇小游戏******************************" << endl;
Sleep(2000);
gotoxy(15, 5);
color(11);
if (m < 3)
{
cout << " 恭喜你闯关成功了" << "进入下一轮" << endl;
}
Sleep(2000);
system("cls");
i = 0;
if (j == 0)
{
r = food.score[j];
}
if (j == 1)
{
r = food.score[j - 1];
q = food.score[j] - food.score[j - 1];
}
if (m == 0)
{
t = 1;
}
if (m == 1)
{
t = 2;
}
if (m == 2)
{
t = 3;
}
m++;
j++;
key = 'w';
}
else
{
system("cls");
gotoxy(10, 3);
color(13);
cout << "**************************贪吃蛇小游戏******************************" << endl;
Sleep(2000);
gotoxy(10, 5);
color(11);
cout << " 很遗憾这局比赛结束了" << endl;
Sleep(1000);
if (t <= 2)
{
Sleep(2000);
gotoxy(15,5);
color(11);
cout << " 你还有 " << 2 - v << "次生命"<< "加油哦"<<endl;
Sleep(1000);
system("cls");
}
if ((j == 1 && w == 1) || (j == 1 && w == 2) || (j == 1 && w == 0))
{
food.score[j] = 0;
r = food.score[j - 1];
w = 0;
}
else if ((j == 2 && w == 2) || (j == 2 && w == 1) || (j==2&&w==0))
{
food.score[j] = 0;
r = food.score[j - 2];
q = food.score[j - 1] - food.score[j - 2];
w = 0;
}
else
{
food.score[j] = 0;
}
w++;
i = 0;
Sleep(1000);
key = 'w';
if (v == 0)
{
t = 1;
}
if (v == 1)
{
t = 2;
}
if (v == 2)
{
t = 3;
}
v++;
}
}
break;
}
if (v== 3)
{
system("cls");
gotoxy(10, 3);
color(13);
cout << "**************************贪吃蛇小游戏******************************" << endl;
gotoxy(15, 5);
color(11);
cout << " 三次生命已经用完了,开启下一轮游戏吧" << endl;
}
if (m == 3)
{
gotoxy(10, 3);
color(13);
cout << "**************************贪吃蛇小游戏******************************" << endl;
gotoxy(15, 5);
color(11);
cout << endl;
cout << endl;
cout<<endl;
cout << " 恭喜你闯关成功" << endl;
cout << endl;
cout << endl;
cout << " 第一局的得分为" << food.score[j - 3] << endl;
cout << endl;
cout << endl;
cout << " 第二局的得分为" << food.score[j - 2] - food.score[j - 3] << endl;
cout << endl;
cout << endl;
cout << " 第三局的得分为" << food.score[j - 1] - food.score[j - 2] << endl;
cout << endl;
cout << endl;
cout << " 最终的得分为" << food.score[j - 1] << endl;
}
gotoxy(0, 0);
system("pause");
return 0;
}
void menu()
{
color(11);
gotoxy(50,5);
cout << "3: 按数字3开始游戏" << endl;
}
上面的就是游戏的源代码,当然了有很多的地方我为了图方便,就直接用最笨的方法设计的,还请小伙伴们帮忙修改。
如果小伙伴们对于我的代码有不懂得地方可以给我留言。