利用C/C++实现贪吃蛇游戏
利用C/C++实现贪吃蛇
(注意:本文章仅供参考,第一次写博客还请多多指教。理解本文章需要easyx和c++等基础知识,并且需要了解贪吃蛇游戏机制)
贪吃蛇机制介绍
相信绝大多数人都曾玩过或者了解过贪吃蛇这款经典的游戏。贪吃蛇顾名思义,就是让蛇尽可能的吃食物。玩家可通过方向键或自定义键来控制蛇头的方向,使它吃到地图出现的随机食物。蛇每吃到一个食物,自身便会增长。当蛇碰到地图的边界或是蛇碰到自身,蛇便会死亡,游戏便结束。
机制大概了解过后,我们将考虑如何实现这类游戏。
设计与分析
首先,我们分析游戏整体结构大概由四个部分构成——界面、地图、蛇、食物。
1、界面:界面能够方便玩家的使用,可让玩家自行选择游戏的开始或结束;通过界面,我们可以设定一些有趣的东西来增加玩家的游戏体验,例如:让玩家选择蛇的速度,以灵活调节游戏难度。
2、地图:地图能为蛇提供活动空间,同时也是蛇位置的坐标轴,方便定位。
3、蛇:蛇是游戏的灵魂。蛇可以定义如下属性:坐标、方向、速度、长度。蛇。蛇的行为有:移动、吃食物、死亡。
4、食物:食物在地图中随机分布,具有坐标(可以尝试去增加颜色属性、大小属性,本文章由于篇幅有限,暂不提供)。
思维导图如下
首先设计用户界面
//部分函数可以暂时不用考虑 void menu() { initgraph(640, 480); int flag = 1; while (flag) { cleardevice(); outtextxy(280, 180, "贪吃蛇游戏"); outtextxy(280, 200, "按1开始游戏"); outtextxy(280, 220, "按2结束游戏"); //接受输入指令0、1 char ch = _getch(); switch (ch) { //开始 case '1': { food F; Initfood(F);//食物初始化 Snake S; hatch(S);//蛇初始化 control_speed(S.speed);//蛇速度控制函数 drawsnake(S, F);//开始绘制游戏面板 break; } //结束 case '2': flag = 0; break; default : break; } } closegraph(); }
接下来可以通过结构体来定义蛇和食物
typedef struct Snake { int speed = 0;//速度 pair<int, int> coor[MAXSIZE]={};//坐标(x, y) int dir;//方向 int length;//长度 }; typedef struct food { pair<int, int> place[MAXSIZE] = {};//坐标 int score;//分数 };
接下来将食物和蛇进行初始化
void Initfood(food& F) { F.score = 0; srand(time(NULL)); //将食物坐标随机设置 for (int i = 0; i < 100; i++) { F.place[i].first = rand() % (640 - R) + R; F.place[i].second = rand() % (480 - R) + R; } } void hatch(Snake &S) { S.length = 3; S.dir = 2; S.speed = 0; //先给出3节身体 for (int i = 0; i < 3; i++) { S.coor[i].first = 40 - i * 10; S.coor[i].second = 30; } }
再接着设计蛇速度控制函数
void control_speed(int &speed) { cleardevice(); outtextxy(280, 180, "请选择蛇的速度1-5"); char ch = _getch(); speed = ch - '0'; }
最关键的蛇运动机制设计来了,我将分段讲解
1、绘制蛇(蛇将有一系列正方体块组成)
for (int i = 0; i < S.length; i++) { int x = S.coor[i].first; int y = S.coor[i].second; //蛇头设为绿色,方便区分 if (i == 0) { setfillcolor(GREEN); solidrectangle(x - R, y - R, x + R, y + R); } else { setcolor(WHITE); rectangle(x - R, y - R, x + R, y + R); }
2、绘制食物(食物由单个正方体组成)
int m = F.place[F.score].first; int n = F.place[F.score].second; setfillcolor(WHITE); solidrectangle(m-R, n-R, m+R, n+R);
3、蛇的运动
像人的运动是通过脚向前迈步来制动整个躯干一样,蛇头同样驱动整个身体。也就是说我们只要控制蛇头的运动方向及前进,躯干便会跟着蛇头一起移动。蛇躯干的每个节点只需要继承上一个节点的位置即可。
void movesnake(Snake& S) { //继承坐标 for (int i = S.length - 1; i > 0; i--) { S.coor[i].first = S.coor[i - 1].first; S.coor[i].second = S.coor[i - 1].second; } switch (S.dir) { case 1: S.coor[0].second-= 10; break; case 2: S.coor[0].first+=10; break; case 3: S.coor[0].second+=10; break; case 4: S.coor[0].first-=10; break; default: break; } }
4、蛇运动控制
使用方向键改变蛇头的运动方向即可
void control_dir(int& DIR) { char ch = _getch(); switch (ch) { case 72: case 'W': case 'w': //这里用if主要是因为蛇运动的机制:当蛇在一某方向运动的途中,不能直接往反方向运动(这样会导致蛇身体重叠),只能通过以当前运动方向为正方向,进行左右移动来改变方向。 if (DIR != 3) DIR = 1; break; case 77: case 'D': case 'd': if (DIR != 4) DIR = 2; break; case 80: case 'S': case 's': if (DIR != 1) DIR = 3; break; case 75: case 'A': case 'a': if (DIR != 2) DIR = 4; break; default: break; } }
4、吃食物
吃完食物的结果:出现下一个食物、蛇变长
void Eating(Snake& S, food& F) { if (S.coor[0].first >= F.place[F.score].first - R&& S.coor[0].first <= F.place[F.score].first + R && S.coor[0].second >= F.place[F.score].second -R&& S.coor[0].second <= F.place[F.score].second+R) { F.score++; S.length++; } }
5、判断是否死亡
死亡判断有两种结果:1、碰壁。2、自身形成回路(自己咬自己)
int isdead(Snake S) { if (S.coor[0].first > 640-R || S.coor[0].first < R || S.coor[0].second>480-R || S.coor[0].second<=R) return 1; else { for (int i = 4; i < S.length; i++) { if (S.coor[0].first >= S.coor[i].first-R&& S.coor[0].first <= S.coor[i].first + R && S.coor[0].second >= S.coor[i].second-R&& S.coor[0].second <= S.coor[i].second+R) return 1; } return 0; } }
蛇的运动机制设计基本大功告成,可以自行在地图上进行修饰,像加入分数栏
void control_dir(int& DIR) { char ch = _getch(); switch (ch) { case 72: case 'W': case 'w': if (DIR != 3) DIR = 1; break; case 77: case 'D': case 'd': if (DIR != 4) DIR = 2; break; case 80: case 'S': case 's': if (DIR != 1) DIR = 3; break; case 75: case 'A': case 'a': if (DIR != 2) DIR = 4; break; default: break; } } void movesnake(Snake& S) { for (int i = S.length - 1; i > 0; i--) { S.coor[i].first = S.coor[i - 1].first; S.coor[i].second = S.coor[i - 1].second; } switch (S.dir) { case 1: S.coor[0].second-= 10; break; case 2: S.coor[0].first+=10; break; case 3: S.coor[0].second+=10; break; case 4: S.coor[0].first-=10; break; default: break; } } int isdead(Snake S) { if (S.coor[0].first > 640-R || S.coor[0].first < R || S.coor[0].second>480-R || S.coor[0].second<=R) return 1; else { for (int i = 4; i < S.length; i++) { if (S.coor[0].first >= S.coor[i].first-R&& S.coor[0].first <= S.coor[i].first + R && S.coor[0].second >= S.coor[i].second-R&& S.coor[0].second <= S.coor[i].second+R) return 1; } return 0; } } void Eating(Snake& S, food& F) { if (S.coor[0].first >= F.place[F.score].first - R&& S.coor[0].first <= F.place[F.score].first + R && S.coor[0].second >= F.place[F.score].second -R&& S.coor[0].second <= F.place[F.score].second+R) { F.score++; S.length++; } } void drawsnake(Snake& S, food F) { char out[3] = {}; BeginBatchDraw(); while (1) { cleardevice(); for (int i = 0; i < S.length; i++) { int x = S.coor[i].first; int y = S.coor[i].second; if (i == 0) { setfillcolor(GREEN); solidrectangle(x - R, y - R, x + R, y + R); } else { setcolor(WHITE); rectangle(x - R, y - R, x + R, y + R); } } int m = F.place[F.score].first; int n = F.place[F.score].second; setfillcolor(WHITE); solidrectangle(m-R, n-R, m+R, n+R); sprintf_s(out, "%d", F.score * S.speed); setbkmode(0); outtextxy(570, 20, "得分"); outtextxy(620, 20, out); FlushBatchDraw(); Sleep(200-S.speed*30); while(_kbhit()) { control_dir(S.dir); } movesnake(S); Eating(S, F); if (isdead(S)) break; } cleardevice(); sprintf_s(out, "%d", F.score * S.speed); outtextxy(280, 180, "得分"); outtextxy(320, 180, out); outtextxy(280, 200, "按Enter键继续"); FlushBatchDraw(); getchar(); EndBatchDraw(); }
完整代码如下
#include <iostream> #include <graphics.h> #include <conio.h> #include <time.h> using namespace std; #define MAXSIZE 100 #define R 5 typedef struct Snake { int speed = 0;//速度 pair<int, int> coor[MAXSIZE]={}; int dir = 2; int length = 3; }; typedef struct food { pair<int, int> place[MAXSIZE] = {}; int score; }; void Initfood(food& F) { F.score = 0; srand(time(NULL)); for (int i = 0; i < 100; i++) { F.place[i].first = rand() % (640 - R) + R; F.place[i].second = rand() % (480 - R) + R; } } void hatch(Snake &S) { S.length = 3; S.dir = 2; S.speed = 0; for (int i = 0; i < 3; i++) { S.coor[i].first = 40 - i * 10; S.coor[i].second = 30; } } void control_speed(int &speed) { cleardevice(); outtextxy(280, 180, "请选择蛇的速度1-5"); char ch = _getch(); speed = ch - '0'; } void control_dir(int& DIR) { char ch = _getch(); switch (ch) { case 72: case 'W': case 'w': if (DIR != 3) DIR = 1; break; case 77: case 'D': case 'd': if (DIR != 4) DIR = 2; break; case 80: case 'S': case 's': if (DIR != 1) DIR = 3; break; case 75: case 'A': case 'a': if (DIR != 2) DIR = 4; break; default: break; } } void movesnake(Snake& S) { for (int i = S.length - 1; i > 0; i--) { S.coor[i].first = S.coor[i - 1].first; S.coor[i].second = S.coor[i - 1].second; } switch (S.dir) { case 1: S.coor[0].second-= 10; break; case 2: S.coor[0].first+=10; break; case 3: S.coor[0].second+=10; break; case 4: S.coor[0].first-=10; break; default: break; } } int isdead(Snake S) { if (S.coor[0].first > 640-R || S.coor[0].first < R || S.coor[0].second>480-R || S.coor[0].second<=R) return 1; else { for (int i = 4; i < S.length; i++) { if (S.coor[0].first >= S.coor[i].first-R&& S.coor[0].first <= S.coor[i].first + R && S.coor[0].second >= S.coor[i].second-R&& S.coor[0].second <= S.coor[i].second+R) return 1; } return 0; } } void Eating(Snake& S, food& F) { if (S.coor[0].first >= F.place[F.score].first - R&& S.coor[0].first <= F.place[F.score].first + R && S.coor[0].second >= F.place[F.score].second -R&& S.coor[0].second <= F.place[F.score].second+R) { F.score++; S.length++; } } void drawsnake(Snake& S, food F) { char out[3] = {}; BeginBatchDraw(); while (1) { cleardevice(); for (int i = 0; i < S.length; i++) { int x = S.coor[i].first; int y = S.coor[i].second; if (i == 0) { setfillcolor(GREEN); solidrectangle(x - R, y - R, x + R, y + R); } else { setcolor(WHITE); rectangle(x - R, y - R, x + R, y + R); } } int m = F.place[F.score].first; int n = F.place[F.score].second; setfillcolor(WHITE); solidrectangle(m-R, n-R, m+R, n+R); sprintf_s(out, "%d", F.score * S.speed); setbkmode(0); outtextxy(570, 20, "得分"); outtextxy(620, 20, out); FlushBatchDraw(); Sleep(200-S.speed*30); while(_kbhit()) { control_dir(S.dir); } movesnake(S); Eating(S, F); if (isdead(S)) break; } cleardevice(); sprintf_s(out, "%d", F.score * S.speed); outtextxy(280, 180, "得分"); outtextxy(320, 180, out); outtextxy(280, 200, "按Enter键继续"); FlushBatchDraw(); getchar(); EndBatchDraw(); } void menu() { initgraph(640, 480); int flag = 1; while (flag) { cleardevice(); outtextxy(280, 180, "贪吃蛇游戏"); outtextxy(280, 200, "按1开始游戏"); outtextxy(280, 220, "按2结束游戏"); char ch = _getch(); switch (ch) { case '1': { food F; Initfood(F); Snake S; hatch(S); control_speed(S.speed); drawsnake(S, F); break; } case '2': flag = 0; break; default : break; } } closegraph(); } int main(void) { menu(); return 0; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
最新评论