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!  

相关文章
|
3月前
|
算法 C++ 容器
C++标准库(速查)总结
C++标准库(速查)总结
96 6
|
3月前
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
134 10
|
2天前
|
JSON C++ 数据格式
C++20 高性能基础库--兰亭集库助力开发者构建高性能应用
这次分享的主题是《高性能基础库--兰亭集库助力开发者构建高性能应用》的实践经验。主要分为三个部分: 1. 业务背景 2. 雅兰亭库架构 3. 业务优化
|
14天前
|
XML 网络协议 API
超级好用的C++实用库之服务包装类
通过本文对Boost.Asio、gRPC和Poco三个超级好用的C++服务包装类库的详细介绍,开发者可以根据自己的需求选择合适的库来简化开发工作,提高代码的效率和可维护性。每个库都有其独特的优势和适用场景,合理使用这些库可以极大地提升C++开发的生产力。
35 11
|
3月前
|
存储 程序员 C++
C++常用基础知识—STL库(2)
C++常用基础知识—STL库(2)
96 5
|
3月前
|
存储 自然语言处理 程序员
C++常用基础知识—STL库(1)
C++常用基础知识—STL库(1)
90 1
|
4月前
|
编译器 API C语言
超级好用的C++实用库之跨平台实用方法
超级好用的C++实用库之跨平台实用方法
56 6
|
9天前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
49 18
|
9天前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
34 13
|
9天前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
31 5