无论什么样的贪吃蛇总会有三个必不可缺的元素
再加上一个简易的的菜单,共四个部分。前三个可以各写一个类,菜单里面我们就直接写函数。
尝试由简入繁,先完成简单的部分,再完成难的,再组装起来。
每一个类写一个头文件一个源文件,头文件只给定义,源文件里再具体实现。
先给出两个常用工具函数,一个用于定位光标方便在我们想要的位置输出,另一个用于隐藏光标,
调用两者需要<windows.h>这个头文件。
//控制输出光标
void Pos(int x, int y)
{
COORD pos; //结构体
pos.X = x; //控制列
pos.Y = y; //控制行
//读取标准输出句柄来控制光标为pos
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}
//隐藏光标
void HideCursor()
{
CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
地图应该有大小,即横纵坐标范围,应该能画出来
Map.h
#pragma once
#include
#include
#include
#pragma comment(lib,"winmm.lib")
using namespace std;
class Map
{
private:
int ROW_Map = 32; //地图的行大小;
int COL_Map = 82; //地图的列大小;
public:
void DrawMap(); //画地图;
};
Map.cpp
#include"Map.h"
void Pos(int x, int y);
void HideCursor();
void Map::DrawMap()
{
system("color 3F"); //设置颜色
int i = 0;
for (i = 20; i < COL_Map + 20; i += 2) //打印上下边框(每个■占用两列)
{
Pos(i, 5); //如果不加20就是打印在控制台边上,下面的5也不用加
printf("■");
Pos(i, 5 + ROW_Map - 1);
printf("■");
}
for (i = 5; i < ROW_Map + 5; i++) //打印左右边框
{
Pos(20, i);
printf("■");
Pos(20 + COL_Map - 2, i);
printf("■");
}
}
写一个测试函数,记得在main函数外加上那两个工具函数,这里为了节省篇幅就不贴出来了
#include"Map.h"
int main()
{
Map A;
A.DrawMap();
system("pause");
return 0;
}
颜色可以自己改,system("color ")里改后面数字就可,具体啥颜色CSDN里可以搜到很多介绍
食物应该是随机产生的,但是应该在地图范围里,而且不能与蛇重合,所以要知道具体的坐标。
随机函数用 rand() ;根据时间给它加一个随机数种子,需要 <ctime> 头文件
Food.h
#pragma once
#include
#include "Map.h"
class Food
{
private:
int pos_x;
int pos_y;
public:
void randfood(); //随机产生一个食物
int getFood_x(); //返回食物的x坐标
int getFood_y(); //返回食物的y坐标
};
Food.cpp
#include"Food.h"
void Pos(int x, int y);
void HideCursor();
void Food::randfood()
{
srand((int)time(NULL));//利用时间添加随机数种子,需要ctime头文件
Loop:
pos_x = rand() % (76) + 24;
pos_y = rand() % (29) + 7;
if (pos_x % 2) //如果食物的x坐标不是偶数则重新确定食物的坐标
goto Loop;
Pos(pos_x, pos_y); //在确认好的位置输出食物
cout << "★";
}
int Food::getFood_x()
{
return pos_x;
}
int Food::getFood_y()
{
return pos_y;
}
用向量容器来描述蛇,直接上代码比较明了
Snake.h
#pragma once
#include
#include
#include"Food.h"
#include
#pragma comment(lib, "Psapi.lib")
void Pos(int x, int y);
void HideCursor();
class Snake
{
private:
struct Snakepoint
{
int x; //第几列
int y; //第几列
};
vector<Snakepoint> snakepoint;
char Direction; //蛇行走的方向
void Show_information(int& score, int& sleeptime); //打印信息
void overout(const int score); //游戏结束显示
void Gameover(int& score,int &x); //游戏结束
void Control(Snakepoint& nexthead); //控制方向
public:
Snake(); //构造函数
void move(Food& food, int& score, int& sleeptime,int& x);//蛇移动并打印出来
void sleep(int& score, int& sleeptime); //根据分数调整游戏速度
};
Snake.cpp
#pragma once
#include"Snake.h"
void Snake::Show_information(int& score,int& sleeptime)
{
Pos(44, 4);
cout << "得分: " << score<<" //空格调整相对位置
cout << "速度: ";
printf("%d", sleeptime);
}
void Snake::overout(const int score) //游戏结束显示
{
Pos(44, 14);
cout << "游戏结束";
Pos(44, 18);
cout << "得分:" << score;
}
void Snake::Gameover(int& score,int &x) //游戏结束原因及得分
{
//撞墙
if (snakepoint[0].x >= 100 || snakepoint[0].x < 22 || snakepoint[0].y >= 36 || snakepoint[0].y < 6)
{
system("cls"); //清屏
Pos(44, 10);
cout << "撞墙啦,头太铁";
overout(score);
score = 0;
Sleep(3000);
exit(0); //退出
}
//撞到自身
for (int i = 1; i < snakepoint.size(); i++)
if (snakepoint[0].x == snakepoint[i].x && snakepoint[0].y == snakepoint[i].y)
{
system("cls"); //清屏
Pos(44, 10);
cout << "撞到自身啦,肯定很痛";
overout(score);
score = 0;
Sleep(3000);
exit(0); //退出
}
}
void Snake::Control(Snakepoint& nexthead)
{
static char Direction = 'R'; //静态变量防止改变移动方向后重新改回来,,将蛇设置为初始向右走
if (_kbhit())
{
char temp = _getch(); //临时变量储存键盘输入的值
if ((temp == 'd') && Direction != 'L')
{
Direction = 'R';
}
else if ((temp == 'a') && Direction != 'R')
{
Direction = 'L';
}
else if ((temp == 'w') && Direction != 'D')
{
Direction = 'U';
}
else if ((temp == 's') && Direction != 'U')
{
Direction = 'D';
}
}
switch (Direction) //根据Direction的值来确定蛇的移动方向
{
case'R':
nexthead.x = snakepoint.front().x + 2; //新的蛇头的头部等于容器内第一个数据(旧蛇头)x坐标+2
nexthead.y = snakepoint.front().y;
break;
case 'L':
nexthead.x = snakepoint.front().x - 2;
nexthead.y = snakepoint.front().y;
break;
case 'U':
nexthead.x = snakepoint.front().x;
nexthead.y = snakepoint.front().y - 1; //因为控制台的x长度是y的一半,所以用两个x做蛇头,需要的坐标是二倍
break;
case 'D':
nexthead.x = snakepoint.front().x;
nexthead.y = snakepoint.front().y + 1;
}
}
void Snake::sleep(int& score, int& sleeptime)
{
if (score < 10)
{
sleeptime = 120;
}
else if (score <20)
{
sleeptime = 80 ;
}
else if (score <30)
{
sleeptime = 50 ;
}
else
{
sleeptime = 30 ;
}
Sleep(sleeptime); //速度随分数加快
}
void Snake::move(Food& food, int& score,int& sleeptime,int& x)
{
Snakepoint nexthead;
Control(nexthead);
snakepoint.insert(snakepoint.begin(), nexthead); //将蛇头插入容器的头部
Show_information(score ,sleeptime);
Gameover(score,x);
if (snakepoint[0].x == food.getFood_x() && snakepoint[0].y == food.getFood_y()) //蛇头与食物重合
{
Pos(snakepoint[0].x, snakepoint[0].y); //吃到食物时这次蛇没有移动,所以蛇会卡顿一下
cout << "¤"; //重新输出一下蛇头和第一节蛇身让蛇不卡顿
Pos(snakepoint[1].x, snakepoint[1].y);
cout << "●";
score++; //吃到食物得分+1
food.randfood(); //重新产生一个食物
return ; //直接结束本次移动
}
//蛇每移动一次,容器向前动一次,打印出来的部分除蛇尾意外其余都被覆盖,所以最后清除蛇尾
for (int i = 0; i < snakepoint.size(); i++) //遍历容器,判断食物与蛇身是否重合并输出整条蛇
{
Pos(snakepoint[i].x, snakepoint[i].y);
if (!i) //蛇头
cout << "¤";
else
cout << "●";
//如果食物刷新到了蛇身上,则重新产生一个食物
if (snakepoint[i].x == food.getFood_x() && snakepoint[i].y == food.getFood_y())
food.randfood();
}
Pos(snakepoint.back().x, snakepoint.back().y); //在容器尾部的地方输出空格 清除画面上的蛇尾
cout << " ";
snakepoint.pop_back(); //删除容器中最后一个数据 清除数据上的蛇尾
}
Snake::Snake()
{
Direction = 'R';
Snakepoint temp; //临时结构变量用于创建蛇
for (int i = 5; i >= 0; i--) //反向创建初始蛇身,初始蛇头朝右
{
temp.x = 22 + (i << 1); //偶数 在蛇头左移生成蛇身
temp.y = 7;
snakepoint.push_back(temp); //在蛇尾尾插入临时变量
}
}
菜单有一个表示选项的光标,用于提示选择
Menu.h
#pragma once
#include
#include
#include
#include
#include
#pragma comment(lib,"winmm.lib")
using namespace std;
void Pos(int x, int y);
void HideCursor();
int Menu_Examine = 0; //用来控制打印菜单的时候只打印一次,不然会在循环的时候一直打印
void DrawMenu() //打印菜单
{
void HideCursor();
if(Menu_Examine==0)
{
Menu_Examine = 1;
for (int i = 5; i > 0; i--)
{
printf("\n");
}
printf("\t\t\t\t☆-☆-☆-☆-☆-☆-☆-☆-☆-☆-☆-☆-☆-☆-☆-☆-☆-☆-☆\n");
printf("\t\t\t\t | |\n");
printf("\t\t\t\t | 贪吃蛇之无敌香蕉蛇皮怪之盘古开天地之最原始版 |\n");
printf("\t\t\t\t | |\n");
printf("\t\t\t\t | |\n");
printf("\t\t\t\t | 开始游戏 |\n");
printf("\t\t\t\t | |\n");
printf("\t\t\t\t | |\n");
printf("\t\t\t\t | 退出游戏 |\n");
printf("\t\t\t\t | |\n");
printf("\t\t\t\t | |\n");
printf("\t\t\t\t | |\n");
printf("\t\t\t\t☆-☆-☆-☆-☆-☆-☆-☆-☆-☆-☆-☆-☆-☆-☆-☆-☆-☆-☆\n");
printf("\t\t\t\t wasd控制,space选择\n");
printf("\t\t\t\t 制作人:琛小獭\n");
}
}
void Chioce(int &x)
{
void HideCursor();
static char n = '1';
if (_kbhit())
{
char temp = _getch(); //临时变量储存键盘输入的值
if ((n == '1') && (temp == 'w'))
{
n = '1'; //选择‘1’的时候不能在往上翻
}
else if ((n == '1') && (temp == 's'))
{
n = '2';
}
else if ((n == '2') && (temp == 'w'))
{
n = '1';
}
else if ((n == '2') && (temp == 's'))
{
n = '2'; //选择‘2’的时候不能在往下翻
}
else if ((temp == ' ') && (n == '1'))
{
x = 2; //进入游戏
return;
}
else if ((temp == ' ') && (n == '2'))
{
exit(0); //退出
}
}
switch (n)
{
case '1': //选择标识在选择‘1’
Pos(52, 10);
cout << "●";
Pos(52, 13);
cout << " ";
break;
case '2': //选择标识在选择‘2’
Pos(52, 10);
cout << " ";
Pos(52, 13);
cout << "●";
break;
}
Sleep(10); //设置间隔以免看出闪烁
}
由于编辑器问题复制过来可能没对齐大家自行调整
BeginGame.h
#pragma once
#include"Map.h"
#include"Food.h"
#include"Snake.h"
Food food; //食物对象
Map map; //地图对象
Snake snake; //蛇对象
int score = 0; //得分变量
int sleeptime = 120; //游戏速度
int Init_Examine;
void BeginGame(int &x)
{
if (Init_Examine == 0)
{
Init_Examine = 1;
system("cls"); //清除之前打印的菜单
HideCursor();
map.DrawMap(); //画地图
food.randfood(); //随机产生一个食物
snake.move(food, score, sleeptime, x); //蛇移动
snake.sleep(score, sleeptime);
}
else if (Init_Examine == 1)
{
snake.move(food, score, sleeptime, x); //蛇移动
snake.sleep(score, sleeptime); //游戏速度
}
}
Main.cpp
#include"Menu.h"
#include"BeginGame.h"
//两个全局函数
//控制输出光标
void Pos(int x, int y)
{
COORD pos; //pos为结构体
pos.X = x; //控制列
pos.Y = y; //控制行
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);//读取输出句柄来控制光标
}
//隐藏光标
void HideCursor()
{
CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
int main()
{
system("title 贪吃蛇"); //设置窗口名字
system("color 3F"); //设置颜色
static int x = 1; //初始打印菜单
void HideCursor(); //隐藏光标
mciSendString("play bgm.mp3 repeat", NULL, 0, NULL);//BGM大家可以自己设置
while (true) //把一个mp3格式文件拷到工程目录下改名为bgm.mp3即可
{
switch (x)
{
case 1:
DrawMenu();
Chioce(x);
Init_Examine = 0;
break;
case 2:
BeginGame(x);
break;
}
}
system("pause");
return 0;
}
大部分地方我没有写提示,代码段里面有注释,各位大佬看明白肯定不在话下
过程中主要参考了下面这两篇文章,感谢这两位大佬,大家有兴趣也看下有助于理解
参考文章1
参考文章2