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!  

相关文章
|
2月前
|
算法 C++ 容器
C++标准库(速查)总结
C++标准库(速查)总结
83 6
|
2月前
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
121 10
|
2月前
|
存储 程序员 C++
C++常用基础知识—STL库(2)
C++常用基础知识—STL库(2)
85 5
|
2月前
|
存储 自然语言处理 程序员
C++常用基础知识—STL库(1)
C++常用基础知识—STL库(1)
77 1
|
3月前
|
编译器 API C语言
超级好用的C++实用库之跨平台实用方法
超级好用的C++实用库之跨平台实用方法
46 6
|
3月前
|
安全 C++
超级好用的C++实用库之环形内存池
超级好用的C++实用库之环形内存池
67 5
|
3月前
|
缓存 网络协议 Linux
超级好用的C++实用库之套接字
超级好用的C++实用库之套接字
39 1
|
29天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
50 2
|
1月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
103 5
|
1月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
90 4