C++实现贪吃蛇(含源代码)—基于easyx图形库

简介: C++实现贪吃蛇(含源代码)—基于easyx图形库

📱一、前置条件—easyx图形库的安装

easyx简介

       easyx 是一款基于 C /C++的图形库,它提供了简单易用的接口,可以方便地进行图形绘制和交互操作。通过引入 easyx,开发者可以快速实现基于 Windows 平台的图形界面应用程序开发。


       easyx 提供了一系列的绘图函数,例如画线、画圆、填充颜色等,开发者可以利用这些函数创建各种图形效果。同时,easyx 还提供了键盘和鼠标事件的处理函数,方便用户与图形界面进行交互。


       easyx 的优势在于它的简洁易用性和适用性。它不仅适用于初学者学习 C /C++图形编程,也可以用于快速原型开发和小型项目。易于安装和使用,对于想要学习图形编程的开发者来说是一个很好的选择。

easyx安装

       easyx安装地址(本文为EasyX 2023大暑版 )

easyx安装步骤

       简单又快捷的安装  😍  我可太爱了!

       根据相应的提示安装即可


🐍二、进入正题!—贪吃蛇的实现!

       1、大致实现思路

       话不多说,先上一张图୧(๑•̀◡•́๑)૭

        通过父类与子类节点继承的关系这样可以使得代码连贯以及紧密相连(其实只是作者想复习一下前段时间学的父类跟子类的知识而已( ̄3 ̄)a)大体的功能实现如上图。

     2、具体实现思路

       父类:
class sprite//父类
{
public:
  int m_x;
  int m_y;
  COLORREF m_color;
public:
  sprite() :sprite(0, 0) {};
  sprite(int x, int y) :m_x(x), m_y(y), m_color(BLUE) {};//初始化坐标,颜色
  virtual void draw()
  {
    setfillcolor(m_color);
    fillrectangle(m_x, m_y, m_x + 10, m_y + 10);
  }
  virtual void draw(COLORREF a)
  {
    setfillcolor(a);
    fillrectangle(m_x, m_y, m_x + 10, m_y + 10);
  }
  void moveby(int dx,int dy)
  {
    m_x += dx;
    m_y += dy;
  }
  bool collision(const sprite& ote)//碰撞食物检测
  {
    return m_x == ote.m_x && m_y == ote.m_y;
  }
};

  作为最基本的类,所要实现的最基本任务就是储存数据,对此我做了坐标数据的储存以及蛇颜色数据的储存。接着是对于基本绘图需求实现函数、移动需求实现函数以及配置检查函数。这几个函数也是对于蛇基本的逻辑判断以及移动的需求。

蛇类 :
class Snake:public sprite//蛇体
{
public:
  Snake() :Snake(0, 0) {};
  Snake(int x, int y):sprite(x, y),dir(VK_RIGHT),grade(0) //初始化三节蛇
  {
    nodes.push_back(sprite(120, 100));//蛇头
    nodes.push_back(sprite(110, 100));
    nodes.push_back(sprite(100, 100));
  };
  void draw()
  {
    for (int i = 0; i < nodes.size(); i++)
    {
      nodes[i].draw();
    }
  }
  void move()
  {
    for (int i=nodes.size()-1;i>0;i--)//整体移动
    {
      nodes[i] = nodes[i - 1];
    }
    switch (dir)//控制方向
    {
    case VK_UP:
      nodes[0].moveby(0,-10);
      break;
    case VK_DOWN:
      nodes[0].moveby(0, 10);
      break;
    case VK_LEFT:
      nodes[0].moveby(-10, 0);
      break;
    case VK_RIGHT:
      nodes[0].moveby(10, 0);
      break;
    }
  }
  bool collision(const sprite& ote)
  {
    return nodes[0].collision(ote);
  }
  void increase()//长度增加一节
  {
    nodes.push_back(sprite());
    grade++;
  }
  void cotget()//计分
  {
    string str = "当前得分为:";
    str += to_string(grade);
    char Outscore[1024];
    strcpy_s(Outscore, sizeof(Outscore), str.c_str());
    settextstyle(15, 0, "宋体");
    settextcolor(GREEN);
    outtextxy(20, 20, Outscore);
  }
  bool dead()//死亡检测
  {
    for (int i = 1; i < nodes.size(); i++)//撞到自己
    {
      if (nodes[0].m_x == nodes[i].m_x && nodes[0].m_y == nodes[i].m_y)
      {
        cleardevice();
        return true;
      }
    }
    if (nodes[0].m_x == 0 || nodes[0].m_y == 0 || nodes[0].m_x == 630 || nodes[0].m_y == 470)//撞到墙
      return true;
    return false;
  }
private:
  vector<sprite> nodes;//存蛇节
public:
  int dir;//蛇的方向
  int grade;//得分
};
2. c

    这个类就是具体的蛇体实现的过程了。主要是实现了蛇的初始化以及所有逻辑判断。在原父类的基础上还进行了蛇的方向、得分、蛇节点等数据的添加。

        食物类:
     这个类就是具体的蛇体实现的过程了。主要是实现了蛇的初始化以及所有逻辑判断。在原父类的基础上还进行了蛇的方向、得分、蛇节点等数据的添加。
         食物类:
cl
   游戏场景的实现(重要):
class gameScreen//游戏场景
{
private:
  Snake snake;
  Food food;
public:
  gameScreen() {};
  void run()
  {
    //双缓冲绘图
    setbkcolor(YELLOW);
    BeginBatchDraw();
    cleardevice();
    wall();
    snake.draw();//蛇生成
    food.draw();//实物生成
    snake.cotget();
    snake.dead();
    EndBatchDraw();
    //移动
    snake.move();
    //吃食物
    SnakeEat();
    //获取信息
    ExMessage msg = { 0 };
    while (peekmessage(&msg, EX_KEY))
    {
      onmesg(msg);
    }
  }
  //改变蛇的方向 通过方向键 
  void onmesg(const ExMessage& msg)
  {
    //判断键盘有没有方向按键按下
    if (msg.message == WM_KEYDOWN)//接受↑↓←→信息
    {
      switch (msg.vkcode)//不让蛇头向后走
      {
      case VK_UP:
        if(snake.dir!= VK_DOWN)
          snake.dir = msg.vkcode;
        break;
      case VK_DOWN:
        if (snake.dir != VK_UP)
          snake.dir = msg.vkcode;
        break;
      case VK_LEFT:
        if (snake.dir != VK_RIGHT)
          snake.dir = msg.vkcode;
        break;
      case VK_RIGHT:
        if (snake.dir != VK_LEFT)
          snake.dir = msg.vkcode;
        break;
      }
    }
  }
  void SnakeEat()
  {
    if (snake.collision(food))//吃到了食物
    {
      //蛇节数增加
      snake.increase();
      //食物重新生成
      food.changefood();
    }
  }
  void deadcot()//死亡结算
  {
    string str = "您的最终得分为:";
    str += to_string(snake.grade);
    char Outscore[1024];
    strcpy_s(Outscore, sizeof(Outscore), str.c_str());
    settextstyle(30, 0, "宋体");
    settextcolor(GREEN);
    outtextxy(200, 210, Outscore);
  }
  void realdead()//判死
  {
    if (snake.dead())
    {
      deadcot();
      Sleep(3000);
      closegraph();
    }
  }
  void wall()//生成墙
  {
    sprite wa;
    for (int i = 0; i < 480; i+=10) {
      //首先声明颜色,否则第一个格子怪怪的
      wa.draw(GREEN);
      wa.moveby(0, 10);
    }
    sprite wb;
    {
      for (int i = 0; i < 640; i += 10) {
        //首先声明颜色,否则第一个格子怪怪的
        wb.draw(GREEN);
        wb.moveby(10, 0);
      }
    }
    sprite sa(630, 470);
    for (int i = 0; i < 480; i += 10) {
      //首先声明颜色,否则第一个格子怪怪的
      sa.draw(GREEN);
      sa.moveby(0, -10);
    }
    sprite sb(630, 470);
    for (int i = 0; i < 640; i += 10) {
      //首先声明颜色,否则第一个格子怪怪的
      sb.draw(GREEN);
      sb.moveby(-10, 0);
    }
  }
};

这个类实现的就是将前面所实现的类进行整合,然后再进行有些稍微的调节。比如:增加了接受方向键的函数omsg,得以操控蛇的运动。这个类也进行了蛇活动场景的搭建等等。

     📑三、 实现效果以及源代码

      1、运行效果

      游戏开始界面:

       游戏运行界面:

      游戏结算界面:


       2、总体代码

#include<iostream>
#include<easyx.h>
#include<vector>
#include<stdlib.h>
#include<time.h>
#include<string>
#include<conio.h>
using namespace std;
class sprite//父类
{
public:
  int m_x;
  int m_y;
  COLORREF m_color;
public:
  sprite() :sprite(0, 0) {};
  sprite(int x, int y) :m_x(x), m_y(y), m_color(BLUE) {};//初始化坐标,颜色
  virtual void draw()
  {
    setfillcolor(m_color);
    fillrectangle(m_x, m_y, m_x + 10, m_y + 10);
  }
  virtual void draw(COLORREF a)
  {
    setfillcolor(a);
    fillrectangle(m_x, m_y, m_x + 10, m_y + 10);
  }
  void moveby(int dx,int dy)
  {
    m_x += dx;
    m_y += dy;
  }
  bool collision(const sprite& ote)//碰撞食物检测
  {
    return m_x == ote.m_x && m_y == ote.m_y;
  }
};
class Snake:public sprite//蛇体
{
public:
  Snake() :Snake(0, 0) {};
  Snake(int x, int y):sprite(x, y),dir(VK_RIGHT),grade(0) //初始化三节蛇
  {
    nodes.push_back(sprite(120, 100));//蛇头
    nodes.push_back(sprite(110, 100));
    nodes.push_back(sprite(100, 100));
  };
  void draw()
  {
    for (int i = 0; i < nodes.size(); i++)
    {
      nodes[i].draw();
    }
  }
  void move()
  {
    for (int i=nodes.size()-1;i>0;i--)//整体移动
    {
      nodes[i] = nodes[i - 1];
    }
    switch (dir)//控制方向
    {
    case VK_UP:
      nodes[0].moveby(0,-10);
      break;
    case VK_DOWN:
      nodes[0].moveby(0, 10);
      break;
    case VK_LEFT:
      nodes[0].moveby(-10, 0);
      break;
    case VK_RIGHT:
      nodes[0].moveby(10, 0);
      break;
    }
  }
  bool collision(const sprite& ote)
  {
    return nodes[0].collision(ote);
  }
  void increase()//长度增加一节
  {
    nodes.push_back(sprite());
    grade++;
  }
  void cotget()//计分
  {
    string str = "当前得分为:";
    str += to_string(grade);
    char Outscore[1024];
    strcpy_s(Outscore, sizeof(Outscore), str.c_str());
    settextstyle(15, 0, "宋体");
    settextcolor(GREEN);
    outtextxy(20, 20, Outscore);
  }
  bool dead()//死亡检测
  {
    for (int i = 1; i < nodes.size(); i++)//撞到自己
    {
      if (nodes[0].m_x == nodes[i].m_x && nodes[0].m_y == nodes[i].m_y)
      {
        cleardevice();
        return true;
      }
    }
    if (nodes[0].m_x == 0 || nodes[0].m_y == 0 || nodes[0].m_x == 630 || nodes[0].m_y == 470)//撞到墙
      return true;
    return false;
  }
private:
  vector<sprite> nodes;//存蛇节
public:
  int dir;//蛇的方向
  int grade;//得分
};
class Food :public sprite//食物
{
public:
  Food() :sprite(0, 0) 
  {
    changefood();
  };
  void draw()
  {
    setfillcolor(RED);
    fillrectangle(m_x, m_y, m_x + 10, m_y + 10);
  }
  //生成食物
  void changefood()
  {
    //随机生成坐标
    m_x = rand() % 64 * 10;
    if (m_x == 0)
      m_x += 10;
    else if (m_x == 640)
      m_x -= 20;
    else if (m_x == 630)
      m_x -= 10;
    m_y = rand() % 48 * 10;
    if (m_y == 0)
      m_y += 10;
    else if (m_y == 480)
      m_y -= 30;
    else if (m_x == 470)
      m_y -= 20;
  }
};
class gameScreen//游戏场景
{
private:
  Snake snake;
  Food food;
public:
  gameScreen() {};
  void run()
  {
    //双缓冲绘图
    setbkcolor(YELLOW);
    BeginBatchDraw();
    cleardevice();
    wall();
    snake.draw();//蛇生成
    food.draw();//实物生成
    snake.cotget();
    snake.dead();
    EndBatchDraw();
    //移动
    snake.move();
    //吃食物
    SnakeEat();
    //获取信息
    ExMessage msg = { 0 };
    while (peekmessage(&msg, EX_KEY))
    {
      onmesg(msg);
    }
  }
  //改变蛇的方向 通过方向键 
  void onmesg(const ExMessage& msg)
  {
    //判断键盘有没有方向按键按下
    if (msg.message == WM_KEYDOWN)//接受↑↓←→信息
    {
      switch (msg.vkcode)//不让蛇头向后走
      {
      case VK_UP:
        if(snake.dir!= VK_DOWN)
          snake.dir = msg.vkcode;
        break;
      case VK_DOWN:
        if (snake.dir != VK_UP)
          snake.dir = msg.vkcode;
        break;
      case VK_LEFT:
        if (snake.dir != VK_RIGHT)
          snake.dir = msg.vkcode;
        break;
      case VK_RIGHT:
        if (snake.dir != VK_LEFT)
          snake.dir = msg.vkcode;
        break;
      }
    }
  }
  void SnakeEat()
  {
    if (snake.collision(food))//吃到了食物
    {
      //蛇节数增加
      snake.increase();
      //食物重新生成
      food.changefood();
    }
  }
  void deadcot()//死亡结算
  {
    string str = "您的最终得分为:";
    str += to_string(snake.grade);
    char Outscore[1024];
    strcpy_s(Outscore, sizeof(Outscore), str.c_str());
    settextstyle(30, 0, "宋体");
    settextcolor(GREEN);
    outtextxy(200, 210, Outscore);
  }
  void realdead()//判死
  {
    if (snake.dead())
    {
      deadcot();
      Sleep(3000);
      closegraph();
    }
  }
  void wall()//生成墙
  {
    sprite wa;
    for (int i = 0; i < 480; i+=10) {
      //首先声明颜色,否则第一个格子怪怪的
      wa.draw(GREEN);
      wa.moveby(0, 10);
    }
    sprite wb;
    {
      for (int i = 0; i < 640; i += 10) {
        //首先声明颜色,否则第一个格子怪怪的
        wb.draw(GREEN);
        wb.moveby(10, 0);
      }
    }
    sprite sa(630, 470);
    for (int i = 0; i < 480; i += 10) {
      //首先声明颜色,否则第一个格子怪怪的
      sa.draw(GREEN);
      sa.moveby(0, -10);
    }
    sprite sb(630, 470);
    for (int i = 0; i < 640; i += 10) {
      //首先声明颜色,否则第一个格子怪怪的
      sb.draw(GREEN);
      sb.moveby(-10, 0);
    }
  }
};
void play()
{
  cleardevice();
  //initgraph(640, 480);
  srand(time(nullptr));
  gameScreen game;
  while (1)
  {
    Sleep(100);
    game.run();
    game.realdead();
  }
  getchar();
}
void menu()
{
  char str_0[] = "》 -贪吃蛇- 《";
  char str_1[] = "1.play";
  char str_2[] = "2.游戏帮助";
  char str_3[] = "按任意键退出";
  settextstyle(20, 0, "宋体");
  outtextxy(200, 150, str_0);
  outtextxy(220, 190, str_1);
  outtextxy(220, 230, str_2);
  outtextxy(220, 270, str_3);
}
void intro()
{
  cleardevice();
  char str_0[] = "【帮助】";
  char str_1[] = "操作技巧:↑↓← →控制蛇的方向";
  char str_2[] = "什么?你说你没方向键?";
  char str_3[] = "亲,请点右上角";
  char str_4[] = "制作者:慕斯";
  char str_5[] = "按任意键返回";
  settextstyle(20, 0, "宋体");
  outtextxy(200, 120, str_0);
  outtextxy(180, 160, str_1);
  outtextxy(180, 200, str_2);
  outtextxy(180, 240, str_3);
  outtextxy(180, 280, str_4);
  outtextxy(180, 320, str_5);
  int leb = 0;
  leb = _getch();
}
void SNAKE()
{
  int leb = 0;
  int kk = 1;
  do
  {
    initgraph(640, 480);
    menu();
    leb = _getch();
    switch (leb)
    {
    case 49:
      play();
      break;
    case 50:
      intro();
      break;
    default:
      kk = 0;
      break;
    }
  } while (kk);
}
int main()
{
  SNAKE();
  //play();
  return 0;
}

 🔑3、一些注意事项

       由于有些字符的限制,我们需要按以下步骤去更改一下字符集,将字符集改为使用多字节字符集以解决其中一些报错!报错情况如下:

        如果你出现以上报错,请按以下步骤进行多字节字符集设置:


               感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o!  

相关文章
|
5天前
|
算法 数据可视化 机器人
Pinocchio - 开源多刚体动力学 C++、Python库
Pinocchio - 开源多刚体动力学 C++、Python库
8 2
|
1月前
|
C++
C++一分钟之-文件系统库(fs)的使用
【7月更文挑战第18天】C++17的`&lt;filesystem&gt;`库简化了文件系统操作,包括`path`类和`directory_iterator`。`path`用于表示路径,`directory_iterator`用于遍历目录。常用功能有路径拼接、分解,创建/删除目录,以及遍历。错误处理、跨平台兼容性和性能是使用时需考虑的关键点。示例代码展示了如何初始化`path`、创建目录、删除目录以及处理异常。
45 1
|
11天前
|
编译器 C++ 开发者
Visual Studio属性表:在新项目中加入已配置好的C++库
通过以上步骤可以确保Visual Studio中新项目成功地加入了之前已配置好的C++库。这个过程帮助开发者有效地管理多个项目中共享的库文件,提升开发效率。
17 0
|
1月前
|
存储 C++ 容器
C++一分钟之-正则表达式库(regex)
【7月更文挑战第7天】C++从C++11开始支持正则表达式,通过`&lt;regex&gt;`库提供功能。本文涵盖基本概念如`std::regex`、`std::smatch`,以及`regex_search`和`regex_match`的使用。常见问题包括大小写敏感性、特殊字符转义、贪婪与非贪婪匹配和捕获组。提供的代码示例展示了如何进行匹配、不区分大小写的匹配、特殊字符匹配、贪婪与非贪婪匹配和捕获组的使用。理解并练习正则表达式能提升文本处理效率。
20 0
|
1月前
|
存储 算法 程序员
C++基础知识(八:STL标准库(Vectors和list))
C++ STL (Standard Template Library标准模板库) 是通用类模板和算法的集合,它提供给程序员一些标准的数据结构的实现如 queues(队列), lists(链表), 和 stacks(栈)等. STL容器的提供是为了让开发者可以更高效率的去开发,同时我们应该也需要知道他们的底层实现,这样在出现错误的时候我们才知道一些原因,才可以更好的去解决问题。
|
1月前
|
算法 前端开发 C++
C++基础知识(八:STL标准库 deque )
deque在C++的STL(Standard Template Library)中是一个非常强大的容器,它的全称是“Double-Ended Queue”,即双端队列。deque结合了数组和链表的优点,提供了在两端进行高效插入和删除操作的能力,同时保持了随机访问的特性。
|
1月前
|
存储 C++ 索引
C++基础知识(八:STL标准库 Map和multimap )
C++ 标准模板库(STL)中的 map 容器是一种非常有用的关联容器,用于存储键值对(key-value pairs)。在 map 中,每个元素都由一个键和一个值组成,其中键是唯一的,而值则可以重复。
|
6天前
|
C++ 容器
C++中自定义结构体或类作为关联容器的键
C++中自定义结构体或类作为关联容器的键
12 0
|
6天前
|
存储 算法 搜索推荐
【C++】类的默认成员函数
【C++】类的默认成员函数
|
5天前
|
存储 安全 编译器
【C++】类和对象(下)
【C++】类和对象(下)
【C++】类和对象(下)