利用C/C++实现贪吃蛇游戏

 更新时间:2021年10月21日 17:10:31   作者:what how when why  
这篇文章主要为大家详细介绍了利用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;
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • 用C语言实现从文本文件中读取数据后进行排序的功能

    用C语言实现从文本文件中读取数据后进行排序的功能

    这是一个十分可靠的程序,这个程序的查错能力非常强悍。程序包含了文件操作,归并排序和字符串输入等多种技术。对大家学习C语言很有帮助,有需要的一起来看看。
    2016-08-08
  • 漫画讲解C语言中最近公共祖先的三种类型

    漫画讲解C语言中最近公共祖先的三种类型

    这篇文章主要总结了使用C语言查找最近公共祖先的三种方法类型,用漫画的方式讲解原理定义,看上去更生动形象,帮助你更好的理解透彻,快来跟着本文往下看吧
    2021-11-11
  • C++遗传算法类文件实例分析

    C++遗传算法类文件实例分析

    这篇文章主要介绍了C++遗传算法的一个类文件,是学习遗传算法的绝佳参考资料,需要的朋友可以参考下
    2014-08-08
  • 浅谈VC++中的内联

    浅谈VC++中的内联

    在 Visual C++ 中使用内联汇编 一、内联汇编的优缺点 因为在Visual C++中使用内联汇编不需要额外的编译器和联接器,且可以处理Visual C++ 中不能处理的一些事情,而且可以使用在 C/C++中的变量,所以非常方便。
    2015-07-07
  • C语言实现扫雷游戏详解(附源码)

    C语言实现扫雷游戏详解(附源码)

    大家好,本篇文章主要讲的是C语言实现扫雷游戏详解(附源码),感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-01-01
  • 你真的知道C++对象大小吗?

    你真的知道C++对象大小吗?

    这篇文章主要给大家介绍了关于C++对象大小的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • C++学习小结之数据类型及转换方式

    C++学习小结之数据类型及转换方式

    本文给大家分享的是本人在学习C++过程中的一个小心得,关于数据类型和转换方式的,这里记录下来,推荐给菜鸟们,高手大神请直接飘过。
    2015-07-07
  • 深入理解C++中变量的存储类别和属性

    深入理解C++中变量的存储类别和属性

    这篇文章主要介绍了C++中变量的存储类别和属性,是C++入门学习中的基础知识,需要的朋友可以参考下
    2015-09-09
  • C++中的命名空间详细介绍

    C++中的命名空间详细介绍

    大家好,本篇文章主要讲的是C++中的命名空间详细介绍,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-02-02
  • C语言 超详细讲解库函数

    C语言 超详细讲解库函数

    C语言库函数是把自定义函数放到库里,是别人把一些常用到的函数编完放到一个文件里,供程序员使用,下面让我们一起来详细了解它
    2022-03-03

最新评论