C项目(贪吃蛇)

简介: C项目(贪吃蛇)

贪吃蛇小游戏可以说是很多小伙伴的游戏启蒙之物,今天我们一起来用C语言复刻童年经典游戏贪吃蛇,在编写这个游戏之前,需要了解一些easyx图形库的知识,因为游戏的窗口就是靠此来提供。

这篇文章主要介绍编写这个游戏要实现的功能,另外我会再写一篇关于这个游戏常见的一些BUG及其解决办法,如果已经能完成部分的编写,而困于某些BUG的伙伴可以移步另一篇文章,可能会有你遇到的一些BUG。

实现贪吃蛇游戏,首先我们需要设置一个游戏窗口,利用easyx图形库,设置一个经典的640*480大小的窗口。窗口设定好了,接下来就可以定义蛇了,我们用结构体来定义蛇的类型,分析一下蛇需要用到那些数据,我们首先需要知道这个蛇有多长,便于游戏开始时蛇的长度设定,及游戏结束后蛇长的统计,那我们就用 int n 来表示蛇的长度。蛇在移动过程中是要有一个移动方向的,因此我们也需要设置一个int direction 来表示蛇移动的方向,因为方向就四个,我们干脆就把四个方向枚举出来,然后蛇的移动方向就用枚举变量来表示

//枚举方向
enum direction
{
  up = 72,
  down = 80,
  left = 75,
  right = 77
};

其次我们需要知道蛇的每一节身体的坐标,这样我们通过坐标来让蛇移动,以及把蛇绘制到窗口上,因为蛇的身体有很多节,坐标又有x , y两个值。我们就把坐标单独定义为结构体,然后身体的每一节都对应着一个坐标,我们用数组来表示身体

//定义蛇的坐标
struct coor
{
  int x;
  int y;
}coor;

最终蛇的定义如下

//定义蛇的数据类型
struct snake
{
  int n = 3;
  direction site;
  coor szb[NUM];
}snake;
//其中NUM是该数组的最大值即蛇的身体最长长度,自行定义

定义完蛇的类型,接下来我们就把蛇初始化以下,写一个初始化函数, initsnake() 假设蛇刚开始长度为3, 方向向左, 坐标依次为

    snake.n = 3;
  snake.site = left;
  snake.szb[0].x = 320;
  snake.szb[0].y = 240;
  snake.szb[1].x = 310;
  snake.szb[1].y = 240;
  snake.szb[2].x = 300;
  snake.szb[2].y = 240; 

初始化完成后,我们就把蛇绘制到窗口上,写一个 Drawsnake() 函数,其实就是一个循环,以蛇的长度为判定条件,从蛇头的坐标开始依次绘制,画矩形或者圆都可,这里我用的是矩形

void Drawsnake()
{ 
  for (int i = 0; i < snake.n; i++)
  {
    rectangle(snake.szb[i].x, snake.szb[i].y, snake.szb[i].x+10, snake.szb[i].y+10);
  }
}

绘制完成之后,接下来就要让蛇动起来了,这点很关键,如何实现让蛇动起来,分析可知,蛇移动的原理是让蛇头的坐标朝向某个方向不断的改变,身体再跟着蛇头一起改变,坐标每变换一次就重新绘制蛇的身体,因为这个过程足够快,我们将其处理的速度用Sleep()再延缓一点,就能形成我们眼睛所能识别的帧率,从而看着蛇是连续移动的。既然要不断的变换蛇头的坐标,并且重新绘制蛇的身体,那就要将这两个函数放到循环里面,蛇的移动我们编写一个Movesnake()。

int main()
{
    initgraph(640, 480);
  initgame();
    while (ture)
   {
     cleardevice();
     Movesnake();
     Drawsnake();
     Sleep(100);
   }
}

那么如何让蛇的坐标不断朝着某个方向改变呢?接下来我们就仔细解决Movesnake(),首先我们要判断具体往哪个方向移动,还记得在定义蛇的时候,设置了一个方向变量site嘛,现在它派上用场了。我们在初始化的时候,将这个变量初始化向左移动,然后程序将蛇头坐标的x减少10,即向左改变了10个像素点,因为在循环里,只要site的值不变,蛇头就一直向左移动,第二节身体的坐标移动到原先蛇头的位置,其他节身体的坐标依次移动到它上一节身体的坐标处,这样就实现了蛇的移动,别忘了加上一个cleardevice()函数,清除上一次绘制的图形。

switch (snake.site)
  {
    case up:
      snake.szb[0].y-=10;
      break;
    case down:
      snake.szb[0].y+=10;
      break;
    case left:
      snake.szb[0].x-=10;
      break;
    case right:
      snake.szb[0].x+=10;
      break;
    }
for (int i = snake.n - 1; i > 0; i--)  //将蛇的每一节身体的坐标替换成上一节身体的坐标
  {
    snake.szb[i].x = snake.szb[i - 1].x;
    snake.szb[i].y = snake.szb[i - 1].y;
  }

蛇的移动完成之后,接下来就是蛇在移动的时候,方向的改变,如果不让蛇的方向改变,那也没法玩呢,首先我们只有在按下方向键的时候蛇头的方向才会改变,那就先设置一个变量用来接收我们按下的方向键,然后再把这个方向键赋值给蛇头的方向变量,我们就编写一个Chdirection()函数来实现

void Chdirection()
{
  char key;
  key = _getch();
  switch(key)
  {
  case up:
    if (snake.site != down)
    {
      snake.site = up;
    }
    break;
  case down:
    if (snake.site != up)
    {
      snake.site = down;
    }
    break;
  case left:
    if (snake.site != right)
    {
      snake.site = left;
    }
    break;
  case right:
    if (snake.site != left)
    {
      snake.site = right;
    }
    break;
  }
}

这里为什么要在每一个分支后面加上 if 判断语句呢,其实就是为了防止蛇直接就调头了,这和现实是不符的,显示中的掉头要绕圈子的。

其实,直接掉头也是可以的,还蛮有意思的,像火车一样两头开,哈哈,感兴趣可以试试。

转向的问题搞定了,但是程序怎么知道什么时候转向呢?因为这个程序是放在循环里的,我们不妨加一个判断语句,用kbhit()函数来检测玩家是否按下了按键,如果按下了就返回真值,进入转向函数,并在其内部判断该转向哪里,没有按下就为假,不进入转向函数。

int main()
{
    initgraph(640, 480);
  initgame();
    while (ture)
   {
     cleardevice();
     Movesnake();
     Drawsnake();
     Sleep(100);
     if ( kbhit() )
      {
        Chdirection();     
      }
   }
}

小蛇能跑起来了,接下就是吃食物了,那吃食物的效果又该如何实现呢?同样,我们要给食物定义一个数据类型,首先就是食物的坐标,其次是食物此刻的状态,是被吃了还是没被吃,状态我们就用bool变量

//定义食物的数据类型
struct food
{
  int x;
  int y;
  bool state;
}food;

接着我们回到初始化函数处,给第一个食物的状态设定一下,定义为true,表示被吃了,为何设定为被吃了,等会解释,我们接着编写Createfood(),用来创造食物,就是给食物的坐标附上随机值,随机值就用随机值生成种子,一旦我们创建一个食物,那创建的瞬间,需要将食物的状态改成false,表示未被吃。

void Creatfood()
{
  while (1)
  {
    food.x = rand() % 62 * 10;
    food.y = rand() % 46 * 10;
    if (food.x >= 20 && food.y >= 20)
    {
      food.state = false;
      break;
    }
  }
}
//这里我加循环是不想让食物生成到边界点

那么什么时候生成食物呢,那当然是食物的状态为true表示被吃了的时候,这也是为什么我们要在开头处将食物的状态设置为true,就是为了开局就生成一个食物。同样要用到一个判断语句

int main()
{
  initgraph(LENGTH, WIDTH);
  initgame();
  while (1)
  {
    if (food.state)
    {
    Creatfood();
    }  
    cleardevice();
    Movesnake();
    Drawsnake();
    Drawfood();
    Sleep(100); 
    EndBatchDraw();
    if (_kbhit())
    {
    Chdirection();
    }
  }

食物创建好了,现在就剩下最后一步了,那就是实现吃的过程,就编写个Eatfood(),当蛇头的坐标与食物的坐标重合时就可以吃了,然后食物的状态改为true,蛇的身长加1.

void Eatfood()
{
  if (snake.szb[0].x == food.x  &&  snake.szb[0].y == food.y)
  {
    snake.n++;
    food.state = true;
  }
}

就这样,简易的贪吃蛇就完成了,还有一个绘制食物,这个蛮简单的,各位伙伴自己实现吧。

int main()
{
  initgraph(LENGTH, WIDTH);
  initgame();
  while (1)
  {
    if (food.state)
    {
    Creatfood();
    }  
        cleardevice();
    Movesnake();
    Drawsnake();
    Drawfood();
    Eatfood();
    Sleep(100); 
        if (_kbhit())
    {
    Chdirection();
    }
  }
  getchar();  
}



目录
相关文章
lda模型和bert模型的文本主题情感分类实战
lda模型和bert模型的文本主题情感分类实战
556 0
|
10月前
|
安全 Linux Shell
CentOS恢复或重置遗忘的root用户密码的方法
至此,整个手术过程完成。没有繁复的迷魂阵,也无需烧香拜佛,就这样直截了当,你已经成功将被遗忘的密码变成了新的秘密。就像你的影子随形,但却更加隐秘安全。这不仅限于CentOS,其实许多Linux系统对于这样的技巧也会乖乖听话。这样的流程就像变魔术一样,让你重新掌握了“开关命运”的钥匙。
738 12
|
SQL 关系型数据库 数据库
SQL数据库:核心原理与应用实践
随着信息技术的飞速发展,数据库管理系统已成为各类组织和企业中不可或缺的核心组件。在众多数据库管理系统中,SQL(结构化查询语言)数据库以其强大的数据管理能力和灵活性,广泛应用于各类业务场景。本文将深入探讨SQL数据库的基本原理、核心特性以及实际应用。一、SQL数据库概述SQL数据库是一种关系型数据库
524 6
|
存储 安全 Android开发
F-Droid:尊重自由与隐私的安卓应用商店
F-Droid 是安卓平台上的自由开源应用商店,专为关注隐私和数据安全的用户设计。本文详细介绍了 F-Droid 的特点,包括其对自由和隐私的重视、无广告和无追踪代码的承诺、强大的应用搜索与管理功能,以及对开源社区的支持。用户可以通过 F-Droid 安全地浏览、安装和管理应用程序,并且开发者也可以发布开源应用。未来,F-Droid 将继续提升用户体验,鼓励更多的开发者与用户参与其中,推动自由开源软件的发展。
2727 1
|
网络协议
TCP连接释放过程
【8月更文挑战第20天】
1202 3
|
人工智能 自然语言处理 算法
开放式API在AI应用开发中的革命性角色
【7月更文第21天】随着人工智能技术的飞速发展,开放式API(Application Programming Interfaces)正逐渐成为连接技术与创新、加速AI应用开发的关键桥梁。这些API允许开发者轻松访问预先训练好的模型和复杂算法,无需从零开始构建基础架构,从而极大地降低了AI应用的开发门槛,促进了技术民主化。本文将探讨开放式API如何在AI领域引发革命性变化,通过实际案例和代码示例展现其强大功能。
671 2
|
监控 Java 测试技术
Java一分钟之-Vert.x:轻量级事件驱动框架
【6月更文挑战第11天】Vert.x是一个轻量级的Java事件驱动框架,用于构建高性能微服务和响应式应用。它采用多-reactor线程模型,支持异步非阻塞IO,提供事件驱动、多语言支持、轻量级特性和模块化组件生态。本文关注Java,讨论了Vert.x中的常见问题,如阻塞事件循环、异常处理和资源泄露,并提供了解决方案。理解异步编程模型,利用Vert.x生态和进行有效监控是避免陷阱的关键。通过实践,开发者能更好地掌握Vert.x并构建高效应用。
669 1
|
运维 安全 Ubuntu
`/var/log/syslog` 和 `/var/log/messages` 日志详解
`/var/log/syslog` 和 `/var/log/messages` 是Linux系统的日志文件,分别在Debian和Red Hat系发行版中记录系统事件和错误。它们包含时间戳、日志级别、PID及消息内容,由`rsyslog`等守护进程管理。常用命令如`tail`和`grep`用于查看和搜索日志。日志级别从低到高包括`debug`到`emerg`,表示不同严重程度的信息。注意保护日志文件的安全,防止未授权访问,并定期使用`logrotate`进行文件轮转以管理磁盘空间。
5822 1
|
Android开发
aTimeLogger--时间追踪工具
aTimeLogger--时间追踪工具
433 0
|
C语言
C项目(贪吃蛇BUG解决及功能扩展)
C项目(贪吃蛇BUG解决及功能扩展)
605 0