当前位置: 首页 > news >正文

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

                                        食用指南:本文在有C++基础的情况下食用更佳  

                                       🍀本文前置知识:C++基础

                                       ♈️今日夜电波:toge—あよ

                                                                0:36 ━━━━━━️💟──────── 4:03
                                                                    🔄   ◀️   ⏸   ▶️    ☰ 

                                      💗关注👍点赞🙌收藏您的每一次鼓励都是对我莫大的支持😍 


目录

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

        easyx简介

        easyx安装

        easyx安装步骤

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

        1、大致实现思路

        2、具体实现思路

        父类:

        蛇类 :

        食物类:

        游戏场景的实现(重要):

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

        1、运行效果

        2、总体代码 

              🔑3、一些注意事项


📱一、前置条件—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;//得分
};

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

         食物类:


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);}}};

        这个类实现的就是将前面所实现的类进行整合,然后再进行有些稍微的调节。比如:增加了接受方向键的函数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!  

                                 

                                                                 给个三连再走嘛~      

http://www.lryc.cn/news/146050.html

相关文章:

  • PHP获取两个日期之间的所有日期
  • STL之stack(适配器讲解以及双端队列的讲解)
  • JVM解密: 解构类加载与GC垃圾回收机制
  • 【Spring Boot】Spring Boot结合MyBatis简单实现学生信息管理模块
  • 【Java List与Map】List<T> Map与Map List<T>的区别(126)
  • 【FreeRTOS】常用函数总结
  • The Cherno——OpenGL
  • linux中学习控制进程的要点
  • C++Qt QSS要注意的坑
  • LeetCode每日一题:56. 合并区间(2023.8.27 C++)
  • 电视盒子什么牌子好?经销商整理线下热销电视盒子品牌排行榜
  • JavaScript关于函数的小挑战
  • 机器学习深度学习——针对序列级和词元级应用微调BERT
  • 重启Mysql时报错rm: cannot remove ‘/var/lock/subsys/mysql‘: Permission denied
  • [C/C++]指针详讲-让你不在害怕指针
  • 无涯教程-Android - Frame Layout函数
  • docker desktop安装es 并连接elasticsearch-head:5
  • 计网(第四章)(网络层)(六)
  • 科研无人机平台P600进阶版,突破科研难题!
  • Apache的简单介绍(LAMP架构+搭建Discuz论坛)
  • CDL基础原理
  • WPF基础入门-Class7-WPF-MVVN框架
  • C语言练习题第三弹!!!绝对典中典!!!
  • Jedis
  • Linux 使用TCP_INFO查询TCP连接的状态信息
  • 软件测试案例 | 气象探测库存管理系统的集成测试计划
  • vue点击按钮重新加载页面(vue第一次加载页面点击按钮出现页面刷新问题之后一切正常)
  • 软件工程(十一) 系统设计分类
  • 数字转中文大写金额
  • Java——HashMap和HashTable的区别