通过easyx窗口实现贪吃蛇

简介: 通过easyx窗口实现贪吃蛇

(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹)

今天我们来尝试一下通过easyx窗口创建贪吃蛇小游戏

首先就是引入几个必要的头文件,相信大家都已经不陌生了,因为在之前的小游戏中都有涉及

#include<stdio.h> 
#include<stdlib.h>
#include<graphics.h>  //便于引入easyx窗口及其函数
#include<conio.h>     //按键控制 
#include<time.h>     //构建随机函数

接下来就是对各个变量进行编写,我们这里的窗口高度定义为640,宽度为480

首先就是最为关键的坐标属性

struct point //坐标属性
{
    int x;
    int y;
};

然后就是蛇和食物的属性

struct Snake //蛇的属性
{
    int num;       //蛇的节数
    point xy[100]; //蛇最长的节数
    char postion;  //方向
}snake;
struct Food //食物属性
{
    point foodxy; //食物坐标
    int flag;     //食物是否存在
}food;

因为后期在移动小蛇时,我们要对方向进行编写,所以我们在这里不妨先枚举一下各个方向,便于后期编写

enum moveposition{right=77,left=75,down=80,up=72}; 

此处也可以不枚举,只需要将后面代码中的的方向改为相应的数字即可

接下来我们就要用不同的函数来构建不同的变量,我将不同的变量总结了一下,便于大家观察

void intsnake();//初始化蛇
void drawsnake();//画蛇
void movesnake();//移动蛇
void keysnake();//按键控制
void intfood();//初始化食物
void drawfood();//画食物
void eatfood();//吃食物
int snakedie();//蛇死亡

首先就是初始化蛇了,这里我们将初始化的蛇确定为三节,并且每一节的间距为10,并初始化蛇运动的方向为右

void intsnake()
{
    snake.xy[2].x = 0;
    snake.xy[2].y = 0;
    snake.xy[1].x = 10;
    snake.xy[1].y = 0;
    snake.xy[0].x = 20;
    snake.xy[0].y = 0;
    snake.num = 3;
    snake.postion = right;
} 

然后就是对蛇进行绘画,我们这里用矩形来表示蛇的身体,对于蛇就颜色,大家可以选择自己喜欢的,我这里用随机函数进行绘制,效果奇特 ,哈哈哈哈哈嗝~

void drawsnake()
{
    for (int i = 0; i < snake.num; i++)
    {
        setlinecolor(BLACK);//边框颜色
        setfillcolor(RGB(rand()%255, rand() % 255, rand() % 255));//填充颜色(随机色)
        //画矩形
        fillrectangle(snake.xy[i].x, snake.xy[i].y, snake.xy[i].x + 10, snake.xy[i].y + 10);
    }
} 

然后就是要考虑蛇移动的问题 ,我们可以这样想,所谓的蛇移动,就是在蛇头移动后,蛇身体的每一个坐标都等于它前面的坐标,这里我们将蛇头记为snake.xy[0],那么第二节的坐标就是蛇头移动前的坐标,第三节的坐标就变成了蛇移动前第二节的坐标。因此这里我们将蛇头与蛇身体分开表示

void movesnake()
{   //除了第一节(蛇头),后面每一节都是前一节的坐标
    for (int i = snake.num - 1; i > 0; i--)
    {
        snake.xy[i].x = snake.xy[i - 1].x;
        snake.xy[i].y = snake.xy[i - 1].y;
    }
    //第一节的处理
    switch (snake.postion)
    {
    case right:
        snake.xy[0].x += 10;
        break;
    case left:
        snake.xy[0].x -= 10;
        break;
    case down:
        snake.xy[0].y += 10;
        break;
    case up:
        snake.xy[0].y -= 10;
        break;
    default:
        break;
    }
} 

在确定完蛇的移动顺序后,就是对蛇进行按键处理了,确保蛇可以被键盘控制,如果对键盘操作有不明白的同学可以查阅(http://t.csdn.cn/KkFwU

void keysnake()
{
    char userkey = 0;
    userkey = _getch();//控制台可以直接读取我按的键
    switch (userkey)
    {
    case 'd':
    case right:
        snake.postion = right;        
        break;
    case left:
    case 'a':
        snake.postion = left;        
        break;
    case down:
    case 's':
        snake.postion = down;    
        break;
    case up:
    case 'w':
        snake.postion = up;
        break;
    default:
        break;
    }
} 

蛇我们已经完成,接下来就是对 食物进行编写,这里我们用随机函数rand对食物出现的位置进行定义,确保食物每次出现的位置不同,之所以将640分成64*10,是确保食物尽可能出现在窗口中间,不然数字太小,蛇可能吃不到。这里我们多加一个如果食物出现在蛇身上,是为了防止在一开始的时候食物便出现在蛇身上导致我们没有看见食物。(虽然这样的几率很小很小很小,但我还是想皮一下 ,嘿嘿嘿)

void intfood()
{
    //拆开写,防止蛇吃不到食物
    food.foodxy.x = rand() % 64 * 10;
    food.foodxy.y = rand() % 48 * 10;
    food.flag = 1;//防止反复出现食物
    //如果食物出现在蛇身上
    for(int i = 0; i > snake.num; i++)
    {
        if(food.foodxy.x==snake.xy[i].x && food.foodxy.y == snake.xy[i].y)
        {
            food.foodxy.x = rand() % 64 * 10;
            food.foodxy.y = rand() % 48 * 10;
        }
    }
}

接下来就是画食物了,和蛇一样,我们也用矩形绘制食物

void drawfood()
{
    setlinecolor(BLACK);
    setfillcolor(GREEN);
    fillrectangle(food.foodxy.x, food.foodxy.y, food.foodxy.x + 10, food.foodxy.y + 10);
}

食物画完了,接下来就该让小蛇吃食物啦

void eatfood()
{
    if (snake.xy[0].x == food.foodxy.x&&snake.xy[0].y==food.foodxy.y)
    {
        snake.num++;//蛇长度每次加一
        food.flag = 0;//重新显示食物
    }
}

接下来就是蛇的死亡啦,小蛇死亡分为两种,撞墙或者撞自己,因为这里用到了MessageBox,所以我们在开头应该声明一个主窗口

HWND hwnd = NULL;//表示主窗口
int snakedie()
{
    if (snake.xy[0].x > 640 || snake.xy[0].y > 480 || snake.xy[0].x < 0 || snake.xy[0].y < 0)
    {
        MessageBox(hwnd, "游戏结束","撞墙",MB_OK);//弹出一个窗口
        return 1;
    }
    for (int i = 1; i < snake.num; i++)//撞自己
    {
        if (snake.xy[0].x == snake.xy[i].x && snake.xy[0].y == snake.xy[i].y)
        {            
            MessageBox(hwnd, "游戏结束", "撞自己", MB_OK);//弹出一个窗口
            return 1;
        }
    }
    return 0;
} 

接下来就是振奋人心的主函数组合啦,我们这里定义flag为0时表示无食物,此时就对食物进行定义并且绘制,在食物出现后,因为 intfood()中flag为1,所以之后食物就不会再次出现,直到在eatfood()中小蛇吃掉食物,并将flag改为0,则食物才会在while()循环的驱动下再次定义并绘制

这里我们还用到了双缓冲绘图,是为了防止小蛇在移动时一闪一闪,这个在我之前的easyx基础中有涉及

int main()
{
    srand((unsigned int)time(NULL));//生成随机数种子
    hwnd = initgraph(640, 480);//窗口大小,init意思是初始化
    setbkcolor(WHITE);//窗口颜色
    intsnake();
    while (1)//循环执行命令
    {
        cleardevice();//刷新一下,防止窗口颜色不变
        //双缓冲绘图,放在绘图代码之前和之后,防止贪吃蛇界面一闪一闪
        BeginBatchDraw();//开始绘图
        drawsnake();
        movesnake();
        drawfood();
        EndBatchDraw();// 结束绘图
        if (food.flag == 0)
        {
            intfood();
        }
        eatfood();
        if (snakedie())
        {
            break;
        }
        while (_kbhit())//判断是否有按键,防止出现屏幕不动的情况
        {
            keysnake();
        }
        Sleep(50);//放慢速度
    }
    getchar();//防止闪屏
    closegraph();//关闭窗口
    return 0;
} 

总体代码就是这样,如果大家发现bug或者有更好的方法 ,欢迎大家一起来讨论呀

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<graphics.h>
#include<conio.h>
struct point //坐标属性
{
  int x;
  int y;
};
struct Snake //蛇的属性
{
  int num;       //蛇的节数
  point xy[100]; //蛇最长的节数
  char postion;  //方向
}snake;
struct Food //食物属性
{
  point foodxy; //食物坐标
  int flag;     //食物是否存在
}food;
HWND hwnd = NULL;//表示主窗口
enum moveposition{right=77,left=75,down=80,up=72};
//void intsnake();//初始化蛇
//void drawsnake();//画蛇
//void movesnake();//移动蛇
//void keysnake();//按键控制
//void intfood();//初始化食物
//void drawfood();//画食物
//void eatfood();//吃食物
//int snakedie();//蛇死亡
void intsnake()
{
  snake.xy[2].x = 0;
  snake.xy[2].y = 0;
  snake.xy[1].x = 10;
  snake.xy[1].y = 0;
  snake.xy[0].x = 20;
  snake.xy[0].y = 0;
  snake.num = 3;
  snake.postion = right;
}
void drawsnake()
{
  for (int i = 0; i < snake.num; i++)
  {
    setlinecolor(BLACK);//边框颜色
    setfillcolor(RGB(rand()%255, rand() % 255, rand() % 255));//填充颜色(随机色)
    //画矩形
    fillrectangle(snake.xy[i].x, snake.xy[i].y, snake.xy[i].x + 10, snake.xy[i].y + 10);
  }
}
void movesnake()
{   //除了第一节(蛇头),后面每一节都是前一节的坐标
  for (int i = snake.num - 1; i > 0; i--)
  {
    snake.xy[i].x = snake.xy[i - 1].x;
    snake.xy[i].y = snake.xy[i - 1].y;
  }
  //第一节的处理
  switch (snake.postion)
  {
  case right:
    snake.xy[0].x += 10;
    break;
  case left:
    snake.xy[0].x -= 10;
    break;
  case down:
    snake.xy[0].y += 10;
    break;
  case up:
    snake.xy[0].y -= 10;
    break;
  default:
    break;
  }
}
void keysnake()
{
  char userkey = 0;
  userkey = _getch();//控制台可以直接读取我按的键
  switch (userkey)
  {
  case 'd':
  case right:
    snake.postion = right;    
    break;
  case left:
  case 'a':
    snake.postion = left;   
    break;
  case down:
  case 's':
    snake.postion = down; 
    break;
  case up:
  case 'w':
    snake.postion = up;
    break;
  default:
    break;
  }
}
void intfood()
{
  //拆开写,防止蛇吃不到食物
  food.foodxy.x = rand() % 64 * 10;
  food.foodxy.y = rand() % 48 * 10;
  food.flag = 1;
  //如果食物出现在蛇身上
  for(int i = 0; i > snake.num; i++)
  {
    if(food.foodxy.x==snake.xy[i].x && food.foodxy.y == snake.xy[i].y)
    {
      food.foodxy.x = rand() % 64 * 10;
      food.foodxy.y = rand() % 48 * 10;
    }
  }
}
void drawfood()
{
  setlinecolor(BLACK);
  setfillcolor(GREEN);
  fillrectangle(food.foodxy.x, food.foodxy.y, food.foodxy.x + 10, food.foodxy.y + 10);
}
void eatfood()
{
  if (snake.xy[0].x == food.foodxy.x&&snake.xy[0].y==food.foodxy.y)
  {
    snake.num++;//蛇长度每次加一
    food.flag = 0;//重新显示食物
  }
}
int snakedie()
{
  if (snake.xy[0].x > 640 || snake.xy[0].y > 480 || snake.xy[0].x < 0 || snake.xy[0].y < 0)
  {
    MessageBox(hwnd, "游戏结束","撞墙",MB_OK);//弹出一个窗口
    return 1;
  }
  for (int i = 1; i < snake.num; i++)//撞自己
  {
    if (snake.xy[0].x == snake.xy[i].x && snake.xy[0].y == snake.xy[i].y)
    {     
      MessageBox(hwnd, "游戏结束", "撞自己", MB_OK);//弹出一个窗口
      return 1;
    }
  }
  return 0;
}
int main()
{
  srand((unsigned int)time(NULL));//生成随机数种子
  hwnd = initgraph(640, 480);//窗口大小,init意思是初始化
  setbkcolor(WHITE);//窗口颜色
  intsnake();
  while (1)
  {
    cleardevice();//刷新一下,防止窗口颜色不变
    //双缓冲绘图,放在绘图代码之前和之后,防止贪吃蛇界面一闪一闪
    BeginBatchDraw();//开始绘图
    drawsnake();
    movesnake();
    drawfood();
    EndBatchDraw();// 结束绘图
    if (food.flag == 0)
    {
      intfood();
    }
    eatfood();
    if (snakedie())
    {
      break;
    }
    while (_kbhit())
    {
      keysnake();
    }
    Sleep(50);//放慢速度
  }
  getchar();//防止闪屏
  closegraph();//关闭窗口
  return 0;
}

大家一起加油

相关文章
|
C# iOS开发 MacOS
MacOS操作系统当中运行VSCode并配置运行调试C#项目
在开发的过程当中,经常会遇到各种开发环境,在MacOS上如何运行VSCode,配置并且调试C#项目,本文进行讲解
2735 0
MacOS操作系统当中运行VSCode并配置运行调试C#项目
|
开发工具 git
成功解决git rebase问题:First, rewinding head to replay your work on top of it...
成功解决git rebase问题:First, rewinding head to replay your work on top of it...
|
9月前
|
人工智能 运维 自然语言处理
《探寻开源AI项目的资金密码:可持续运营之路》
在人工智能浪潮中,开源项目汇聚全球智慧,推动AI创新。然而,资金困境限制了其发展。企业赞助、社区捐赠、政府资助、付费服务等模式可为开源项目提供稳定资金来源。通过成本控制、合作伙伴关系及品牌建设,开源项目能实现可持续运营,突破发展瓶颈,为AI领域注入源源不断的活力。
199 12
|
11月前
|
存储 Unix Linux
进程间通信方式-----管道通信
【10月更文挑战第29天】管道通信是一种重要的进程间通信机制,它为进程间的数据传输和同步提供了一种简单有效的方法。通过合理地使用管道通信,可以实现不同进程之间的协作,提高系统的整体性能和效率。
|
JavaScript
Vue3相关组件项目依赖依赖版本信息
这篇文章展示了一个Vue 3项目中使用的组件和库的依赖版本信息,包括`ant-design-vue`、`swiper`、`vue`、`vue-router`等,以及开发依赖如`vite`、`vue-tsc`、`eslint`等。
208 1
Vue3相关组件项目依赖依赖版本信息
|
开发工具 C++
qt开发技巧与三个问题点
本文介绍了三个Qt开发中的常见问题及其解决方法,并提供了一些实用的开发技巧。
349 0
|
JavaScript
vue 全局响应键盘按键/监听键盘事件(含 js 获取键盘keyCode值的方法)
vue 全局响应键盘按键/监听键盘事件(含 js 获取键盘keyCode值的方法)
996 2
|
人工智能 前端开发 API
AI智能体研发之路-工程篇(五):大模型推理服务框架LocalAI一键部署
AI智能体研发之路-工程篇(五):大模型推理服务框架LocalAI一键部署
873 0
|
缓存 JSON Java
一文教会你 Spring Boot中的热部署与单元测试(简单易懂,附源码实战)
一文教会你 Spring Boot中的热部署与单元测试(简单易懂,附源码实战)
228 0
|
关系型数据库 MySQL 编译器
【Qt 数据库 】qt使用mysql的准备工作
【Qt 数据库 】qt使用mysql的准备工作
380 1

热门文章

最新文章