效果演示:
![](https://i-blog.csdnimg.cn/blog_migrate/d42cfd74c91a1c6ef7b56b8cb1390fc8.png)
![](https://i-blog.csdnimg.cn/blog_migrate/1ad2ae759856475f10691b059f9d4ea6.png)
项目架构讲解:
定义一个结点类node表示蛇的每一节 成员属性:坐标x,y 成员函数 :无参构造 有参构造
函数:void start();打印游戏初始界面
void initsnake()//初始化蛇
void randomfood(); 随机生成食物
void paintsnake();绘制蛇
void move(char select);移动函数 select为按键
bool if_gameover();判断游戏有效性的函数
void recordtopscore();记录最高分函数
void end();打印结束界面函数
3、话不多说,上代码
(记得把项目的字符集改成多字符集,否则esaxyx的一些函数会报错,或者你把easyx的函数的字符串参数全部加上通配符_T()
头文件:node.h
#pragma once
#include<iostream>
using namespace std;
#include<deque>
//实现结点类 初始蛇的函数
#include<iostream>
#include<deque>
#define AA "topscore.txt"
class node
{
public:
int x;//横坐标
int y;//纵坐标
node()
{
}
node(int a,int b)//有参构造函数
{
x = a;
y = b;
};
};
deque<node>snake;//创建双端容器来储存蛇的每一个结点 定义在全局区域 免去了每个函数都要传入这个参数的麻烦
node food;//创建食物对象
int score=3;//本场游戏得分 初始分数就是初始长度3
bool is_eaten = true;
char Move = 'S';//默认向左移动 move来储存移动方向
void initsnake()//初始化蛇 先定蛇的初始长度为3
{
node n1(2,3);
node n2(3, 3);
node n3(4, 3);
snake.push_back(n1);
snake.push_back(n2);
snake.push_back(n3);
}
头文件: paint.h
#pragma once
using namespace std;
#include<iostream>
#include<easyx.h>
#include<graphics.h>//这个头文件主要写绘制蛇的函数√ 蛇移动的函数√ 随机生成食物的函数√ 和绘制开始√和结束的界面√ 以及文件操作√ 判断游戏是否结束的函数 √
#include"node.h"
#include<fstream>
#include<time.h>
//整个画布是800*600 将其分为40*30的小正方形 这样就有 400个小矩形了 把每个矩形的左上角的坐标作为每一个矩形的坐标 横坐标的范围是:0~19 纵坐标的范围是:0~19
//初始界面函数:
void start()
{
initgraph(800 , 600);//要实现enter键后跳转到游戏界面 就不能使用同一个画板
setbkcolor(RGB(199, 215, 238)); //设置窗口背景色为白色
cleardevice(); //刷新屏幕
setbkmode(TRANSPARENT); //设置字体背景色为透明
settextcolor(BLACK); //设置字体颜色为红色
/*****************游戏规则*****************/
outtextxy(200, 280, _T("按任意键进入游戏"));
outtextxy(200, 300, _T("字母键 W,S,A,D 方向键 上下左右 控制方向 按其他任意键可以暂停游戏"));
getchar();
closegraph();
}
//随机生成食物的函数
void randomfood()
{
food.x = 0 + rand() % 20;//产生0~19(闭区间)的随机数
food.y= 0 + rand() % 20;//产生0~19(闭区间)的随机数;
setfillcolor(RED);//设置矩形填充色
fillrectangle(food.x * 40, food.y * 30, (food.x + 1) * 40, (food.y + 1) * 30);
is_eaten = false;
}
void paintsnake()
{
setfillcolor(BLUE);//设置填充色
//snake deque容器
for (deque<node>::iterator it = snake.begin(); it != snake.end(); it++)//绘制蛇身
{
int x1;
int y1;
x1 = it->x * 40;
y1 = it->y * 30;
fillrectangle(x1, y1, x1 + 40, y1 + 30);
}
}
void move(char select)
{
deque<node>::iterator it;
it = snake.begin();
int xt = it->x;
int yt = it->y;//先暂存为移动之前的蛇头坐标
//先不管是否游戏结束 待会用另一个函数来判断游戏是否结束
if (select == 'w' || select == 'W')//向上移动 这里有两种情况 1、没吃到食物 2、吃到食物了
{
node ns(xt, yt - 1);//创建新蛇头结点
//先判断是否迟到食物 bool is_eaten
if (food.x == ns.x && food.y == ns.y)//1、吃到食物的情况 这样的话就用删除最后一个元素了 只要子啊加入一个新蛇头就行了
{
snake.push_front(ns);
score++;
is_eaten = true;
}
else //2、没吃到食物:蛇头的横坐标不变 纵坐标-1 实际上就是把最后一个元素去掉 然后用头插法插入一个新蛇头
{
snake.push_front(ns);//利用头插法插入新的蛇头
snake.pop_back();//删除旧的蛇尾
}
//打印蛇身
paintsnake();
}
if (select == 's' || select == 'S')//向下移动 这里有两种情况 1、没吃到食物 2、吃到食物了
{
node ns(xt, yt +1);//创建新蛇头结点
//先判断是否迟到食物 bool is_eaten
if (food.x == ns.x && food.y == ns.y)//1、吃到食物的情况 这样的话就用删除最后一个元素了 只要子啊加入一个新蛇头就行了
{
snake.push_front(ns);
score++;
is_eaten = true;
}
else //2、没吃到食物:蛇头的横坐标不变 纵坐标-1 实际上就是把最后一个元素去掉 然后用头插法插入一个新蛇头
{
snake.push_front(ns);//利用头插法插入新的蛇头
snake.pop_back();//删除旧的蛇尾
}
//打印蛇身
paintsnake();
}
if (select == 'a' || select == 'A')//向左移动 这里有两种情况 1、没吃到食物 2、吃到食物了
{
node ns(xt-1, yt);//创建新蛇头结点
//先判断是否迟到食物 bool is_eaten
if (food.x == ns.x && food.y == ns.y)//1、吃到食物的情况 这样的话就用删除最后一个元素了 只要子啊加入一个新蛇头就行了
{
snake.push_front(ns);
score++;
is_eaten = true;
}
else //2、没吃到食物:蛇头的横坐标不变 纵坐标-1 实际上就是把最后一个元素去掉 然后用头插法插入一个新蛇头
{
snake.push_front(ns);//利用头插法插入新的蛇头
snake.pop_back();//删除旧的蛇尾
}
//打印蛇身
paintsnake();
}
if (select == 'd' || select == 'D')//向右移动 这里有两种情况 1、没吃到食物 2、吃到食物了
{
node ns(xt+1, yt);//创建新蛇头结点
//先判断是否迟到食物 bool is_eaten
if (food.x == ns.x && food.y == ns.y)//1、吃到食物的情况 这样的话就用删除最后一个元素了 只要子啊加入一个新蛇头就行了
{
snake.push_front(ns);
score++;
is_eaten = true;
}
else //2、没吃到食物:蛇头的横坐标不变 纵坐标-1 实际上就是把最后一个元素去掉 然后用头插法插入一个新蛇头
{
snake.push_front(ns);//利用头插法插入新的蛇头
snake.pop_back();//删除旧的蛇尾
}
//打印蛇身
paintsnake();
}
}
//游戏结束界面函数
void end()
{
setbkmode(TRANSPARENT);//设置为你背景透明
outtextxy(200, 280, _T("很遗憾,游戏结束!!!"));
ifstream ifs;
ifs.open(AA, ios::in);
int a;
ifs >> a;
char str[5];
outtextxy(180, 300, _T("目前最高分为:"));
sprintf_s(str, "%d",a);
outtextxy(300, 300, str);//重点
}
void recordtopscore()
{
//记录最高分
ifstream ifs;
ifs.open(AA,ios::in);//读取历史最高分
int a;
ifs >> a;
a = a > score ? a : score;//刷新最高分
ifs.close();
ofstream ofs;
ofs.open(AA, ios::trunc);//以覆盖的方式写入文件
ofs << a;
ofs.close();
}
bool if_gameover()//这里有两种情况:1、撞到墙了 2、撞到自己了
{
deque<node>::iterator it = snake.begin();
node p(0, 0);
p.x = it->x; p.y = it->y;
if (it->x < 0 || it->x >19 || it->y < 0 || it->y>19)//撞到墙了
{
return true;
}
it++;
for (it; it != snake.end(); it++)//遍历每一节看蛇头是否和蛇身的某一节重合了
{
if (it->x == p.x && it->y == p.y)
{
return true;
}
}
return false;//返回false就是没有游戏结束
}
源文件:main.cpp
#include<iostream>
using namespace std;
#include"node.h"
#include"paint.h"
#include<conio.h>
#include<time.h>
/*
游戏设计:
1、代码实现基本原理:鉴于蛇每移动一次如果没有迟到食物那么起始就只要把蛇的最后一节去掉然后根据移动方向加一个新的蛇头 吃到食物的时候跟简单只要根据移动方向加一个新的蛇头就行了
所以我们先定义一个结点类然后用一个双端容器来储存蛇的每一个结点
2、代码实现的重点:移动函数,随机生成食物的函数,判断是否游戏失败的函数,游戏等级的记录(文件操作),游戏开始界面(欢迎语,开始按钮) ,游戏结束页面(显示死亡原因和游戏分数)
*/
//实现过程中遇到的问题:蛇不动 移动方向和控制方向相反 食物生成
bool b1, b2, b3, b4;
void control()//控制函数 来管理整个流程
{
char ch;//控制方向的字符
while(true)//如果判断到了游戏结束就终止循环
{
cleardevice();//清屏
if (is_eaten == true)
{
randomfood();
}
else
{
setfillcolor:RED;
fillrectangle(food.x * 40, food.y * 30, food.x * 40 + 40, food.y * 30 + 30);
}
paintsnake();
if (_kbhit())//判断键盘是否有输入
{
ch = _getch();
b1 = (ch == 'w' || ch == 'W') && (Move == 's' || Move == 'S');
b2 = (ch == 's' || ch == 'S') && (Move == 'w' || Move == 'W');
b3 = (ch == 'a' || ch == 'A') && (Move == 'd' || Move == 'D');
b4 = (ch == 'd' || ch == 'D') && (Move == 'a' || Move == 'A');
if(!(b1 || b2 || b3 || b4))
Move = ch;
}
//不能向当前蛇头移动方向的反方向运动
move(Move);
if (if_gameover())
{
break;
}
Sleep(100);//暂停100毫秒
}
recordtopscore();//刷新最高分
end();
}
int main()
{
srand(time(0));
start();
initgraph(800, 600);//绘制800*600的画布 使用默认的坐标系 x轴在最上面 向右为正方向 y轴在最左边向下为正方向 左上角为原点
setbkcolor(RGB(88, 101, 0));
cleardevice();
initsnake();//初始化蛇
control();
getchar();//暂停画布
closegraph();
system("pause");
}
4、建议:
还可以加一些功能:
比如:结束界面可以先试一下本局的分数
可以增加速度渐变机制:比如短的时候跑得快 长的时候跑得慢
可以贴图
可以在地图中间增加一些障碍
5、还存在的一些问题:
蛇身会闪烁(由于项目完成时间过去了很久,暂时发现不了,如果有解决的uu可以发在评论区哦,谢谢啦)
对比其他博主写的贪吃蛇的优点:
使用了deque双端容器来储存蛇的每一个结点
因为蛇移动的原理就是尾节点加到头结点前面