基于easyx图形库c++手搓贪吃蛇

  1. 效果演示:

  1. 项目架构讲解:

  • 定义一个结点类node表示蛇的每一节 成员属性:坐标x,y 成员函数 :无参构造 有参构造

  • 函数:void start();打印游戏初始界面

void initsnake()//初始化蛇

void randomfood(); 随机生成食物

void paintsnake();绘制蛇

void move(char select);移动函数 select为按键

bool if_gameover();判断游戏有效性的函数

void recordtopscore();记录最高分函数

void end();打印结束界面函数

3、话不多说,上代码

(记得把项目的字符集改成多字符集,否则esaxyx的一些函数会报错,或者你把easyx的函数的字符串参数全部加上通配符_T()

头文件:node.h

#pragma once
#include<iostream>
using namespace std;
#include<deque>
//实现结点类 初始蛇的函数
#include<iostream>
#include<deque>
#define  AA "topscore.txt"
class node
{
public:
    int x;//横坐标
    int y;//纵坐标
    node()
    {
    }
    node(int a,int b)//有参构造函数
    {
        x = a;
        y = b;
    };
};
deque<node>snake;//创建双端容器来储存蛇的每一个结点  定义在全局区域 免去了每个函数都要传入这个参数的麻烦
node food;//创建食物对象
int score=3;//本场游戏得分  初始分数就是初始长度3
bool is_eaten = true;
char Move = 'S';//默认向左移动 move来储存移动方向
void initsnake()//初始化蛇 先定蛇的初始长度为3
{
    node n1(2,3);
    node n2(3, 3);
    node n3(4, 3);
    snake.push_back(n1);
    snake.push_back(n2);
    snake.push_back(n3);
}

头文件: paint.h

#pragma once
using namespace std;
#include<iostream>
#include<easyx.h>
#include<graphics.h>//这个头文件主要写绘制蛇的函数√ 蛇移动的函数√ 随机生成食物的函数√ 和绘制开始√和结束的界面√ 以及文件操作√  判断游戏是否结束的函数 √
#include"node.h"
#include<fstream>
#include<time.h>
//整个画布是800*600 将其分为40*30的小正方形 这样就有 400个小矩形了   把每个矩形的左上角的坐标作为每一个矩形的坐标  横坐标的范围是:0~19 纵坐标的范围是:0~19
//初始界面函数:

void start()
{
    initgraph(800 , 600);//要实现enter键后跳转到游戏界面 就不能使用同一个画板
    setbkcolor(RGB(199, 215, 238));        //设置窗口背景色为白色
    cleardevice();            //刷新屏幕
    setbkmode(TRANSPARENT);    //设置字体背景色为透明
    settextcolor(BLACK);        //设置字体颜色为红色
    /*****************游戏规则*****************/
    outtextxy(200, 280, _T("按任意键进入游戏"));
    outtextxy(200, 300, _T("字母键 W,S,A,D 方向键 上下左右 控制方向 按其他任意键可以暂停游戏"));
    getchar();
    closegraph();
}

//随机生成食物的函数
void randomfood()  
{
    food.x = 0 + rand() % 20;//产生0~19(闭区间)的随机数
    food.y= 0 + rand() % 20;//产生0~19(闭区间)的随机数;
    setfillcolor(RED);//设置矩形填充色
    fillrectangle(food.x * 40, food.y * 30, (food.x + 1) * 40, (food.y + 1) * 30);
    is_eaten = false;
}

void paintsnake()
{
    setfillcolor(BLUE);//设置填充色
    //snake deque容器
    for (deque<node>::iterator it = snake.begin(); it != snake.end(); it++)//绘制蛇身
    {
        int x1;
        int y1;
        x1 = it->x * 40;
        y1 = it->y * 30;
        fillrectangle(x1, y1, x1 + 40, y1 + 30);
    }
}
void move(char select)
{
    deque<node>::iterator it;
    it = snake.begin();
    int xt = it->x;
    int yt = it->y;//先暂存为移动之前的蛇头坐标
    //先不管是否游戏结束  待会用另一个函数来判断游戏是否结束
    if (select == 'w' || select == 'W')//向上移动  这里有两种情况 1、没吃到食物  2、吃到食物了 
    {
        node ns(xt, yt - 1);//创建新蛇头结点
        //先判断是否迟到食物 bool is_eaten
        if (food.x == ns.x && food.y == ns.y)//1、吃到食物的情况 这样的话就用删除最后一个元素了 只要子啊加入一个新蛇头就行了
        {
            snake.push_front(ns);
            score++;
            is_eaten = true;
        }
        else    //2、没吃到食物:蛇头的横坐标不变 纵坐标-1  实际上就是把最后一个元素去掉 然后用头插法插入一个新蛇头
        {
            snake.push_front(ns);//利用头插法插入新的蛇头
            snake.pop_back();//删除旧的蛇尾
        }
        //打印蛇身
        paintsnake();
    }


    if (select == 's' || select == 'S')//向下移动  这里有两种情况 1、没吃到食物  2、吃到食物了 
    {
         node ns(xt, yt +1);//创建新蛇头结点
        //先判断是否迟到食物 bool is_eaten
        if (food.x == ns.x && food.y == ns.y)//1、吃到食物的情况 这样的话就用删除最后一个元素了 只要子啊加入一个新蛇头就行了
        {
            snake.push_front(ns);
            score++;
            is_eaten = true;
        }
        else    //2、没吃到食物:蛇头的横坐标不变 纵坐标-1  实际上就是把最后一个元素去掉 然后用头插法插入一个新蛇头
        {
            snake.push_front(ns);//利用头插法插入新的蛇头
            snake.pop_back();//删除旧的蛇尾
        }
        //打印蛇身
        paintsnake();
    }


    if (select == 'a' || select == 'A')//向左移动  这里有两种情况 1、没吃到食物  2、吃到食物了 
    {
        node ns(xt-1, yt);//创建新蛇头结点
        //先判断是否迟到食物 bool is_eaten
        if (food.x == ns.x && food.y == ns.y)//1、吃到食物的情况 这样的话就用删除最后一个元素了 只要子啊加入一个新蛇头就行了
        {
            snake.push_front(ns);
            score++;
            is_eaten = true;
        }
        else    //2、没吃到食物:蛇头的横坐标不变 纵坐标-1  实际上就是把最后一个元素去掉 然后用头插法插入一个新蛇头
        {
            snake.push_front(ns);//利用头插法插入新的蛇头
            snake.pop_back();//删除旧的蛇尾
        }
        //打印蛇身
        paintsnake();
    }


    if (select == 'd' || select == 'D')//向右移动  这里有两种情况 1、没吃到食物  2、吃到食物了 
    {
        node ns(xt+1, yt);//创建新蛇头结点
        //先判断是否迟到食物 bool is_eaten
        if (food.x == ns.x && food.y == ns.y)//1、吃到食物的情况 这样的话就用删除最后一个元素了 只要子啊加入一个新蛇头就行了
        {
            snake.push_front(ns);
            score++;
            is_eaten = true;
        }
        else    //2、没吃到食物:蛇头的横坐标不变 纵坐标-1  实际上就是把最后一个元素去掉 然后用头插法插入一个新蛇头
        {
            snake.push_front(ns);//利用头插法插入新的蛇头
            snake.pop_back();//删除旧的蛇尾
        }
        //打印蛇身
        paintsnake();
    }
}
//游戏结束界面函数
void end()
{
    setbkmode(TRANSPARENT);//设置为你背景透明
    outtextxy(200, 280, _T("很遗憾,游戏结束!!!"));
    ifstream ifs;
    ifs.open(AA, ios::in);
    int a;
    ifs >> a;
    char str[5];
    outtextxy(180, 300, _T("目前最高分为:"));
    sprintf_s(str, "%d",a);
    outtextxy(300, 300, str);//重点
}
void recordtopscore()
{
    //记录最高分
    ifstream ifs;
    ifs.open(AA,ios::in);//读取历史最高分
    int a;
    ifs >> a;
    a = a > score ? a : score;//刷新最高分
    ifs.close();
    ofstream ofs;
    ofs.open(AA, ios::trunc);//以覆盖的方式写入文件
    ofs << a;
    ofs.close();
}
bool if_gameover()//这里有两种情况:1、撞到墙了  2、撞到自己了
{
    deque<node>::iterator it = snake.begin();
    node p(0, 0);
    p.x = it->x; p.y = it->y;
    if (it->x < 0 || it->x >19 || it->y < 0 || it->y>19)//撞到墙了
    {
        return true;
    }
    it++;
    for (it; it != snake.end(); it++)//遍历每一节看蛇头是否和蛇身的某一节重合了
    {
        if (it->x == p.x && it->y == p.y)
        {
            return true;
        }
    }
    return false;//返回false就是没有游戏结束
}

源文件:main.cpp

#include<iostream>
using namespace std;
#include"node.h"
#include"paint.h"
#include<conio.h>
#include<time.h>
/*
游戏设计:
1、代码实现基本原理:鉴于蛇每移动一次如果没有迟到食物那么起始就只要把蛇的最后一节去掉然后根据移动方向加一个新的蛇头 吃到食物的时候跟简单只要根据移动方向加一个新的蛇头就行了
所以我们先定义一个结点类然后用一个双端容器来储存蛇的每一个结点
2、代码实现的重点:移动函数,随机生成食物的函数,判断是否游戏失败的函数,游戏等级的记录(文件操作),游戏开始界面(欢迎语,开始按钮) ,游戏结束页面(显示死亡原因和游戏分数)
*/
//实现过程中遇到的问题:蛇不动  移动方向和控制方向相反  食物生成
bool b1, b2, b3, b4;
void control()//控制函数  来管理整个流程
{
    char ch;//控制方向的字符
    while(true)//如果判断到了游戏结束就终止循环
    {
        cleardevice();//清屏
       if (is_eaten == true)
        {
            randomfood();
        }
       else
       {

       setfillcolor:RED;
           fillrectangle(food.x * 40, food.y * 30, food.x * 40 + 40, food.y * 30 + 30);
       }
        paintsnake();
        if (_kbhit())//判断键盘是否有输入
        {
        ch = _getch();
        b1 = (ch == 'w' || ch == 'W') && (Move == 's' || Move == 'S');
        b2 = (ch == 's' || ch == 'S') && (Move == 'w' || Move == 'W');
        b3 = (ch == 'a' || ch == 'A') && (Move == 'd' || Move == 'D');
        b4 = (ch == 'd' || ch == 'D') && (Move == 'a' || Move == 'A');
        if(!(b1 || b2 || b3 || b4))
            Move = ch;
        }
        //不能向当前蛇头移动方向的反方向运动
        
        move(Move);
        if (if_gameover())
        {
           break;
        }
        Sleep(100);//暂停100毫秒
    }
recordtopscore();//刷新最高分
    end();
}
int main()
{

    srand(time(0));
    start();
    initgraph(800, 600);//绘制800*600的画布  使用默认的坐标系 x轴在最上面 向右为正方向 y轴在最左边向下为正方向 左上角为原点
    setbkcolor(RGB(88, 101, 0));
    cleardevice();
initsnake();//初始化蛇
control();
    getchar();//暂停画布
closegraph();
    system("pause");
}

4、建议:

还可以加一些功能:

比如:结束界面可以先试一下本局的分数

可以增加速度渐变机制:比如短的时候跑得快 长的时候跑得慢

可以贴图

可以在地图中间增加一些障碍

5、还存在的一些问题:

蛇身会闪烁(由于项目完成时间过去了很久,暂时发现不了,如果有解决的uu可以发在评论区哦,谢谢啦)

  1. 对比其他博主写的贪吃蛇的优点:

使用了deque双端容器来储存蛇的每一个结点

因为蛇移动的原理就是尾节点加到头结点前面

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论
好的,以下是一个使用 EasyX 图形库实现贪吃蛇游戏的示例代码: ```c++ #include <graphics.h> #include <conio.h> #include <time.h> const int WIDTH = 600; // 游戏窗口宽度 const int HEIGHT = 600; // 游戏窗口高度 const int CELL_SIZE = 20; // 每个蛇身格子的大小 const int ROWS = HEIGHT / CELL_SIZE - 1; // 游戏窗口中行数 const int COLS = WIDTH / CELL_SIZE - 1; // 游戏窗口中列数 struct Snake { int x, y; struct Snake *next; }; enum Direction { UP, DOWN, LEFT, RIGHT }; Direction direction = RIGHT; // 蛇的初始方向 Snake *head = NULL; // 蛇头指针 int score = 0; // 得分 void initSnake() { head = new Snake; head->x = 5; head->y = 5; head->next = NULL; } void drawSnake() { setfillcolor(YELLOW); for (Snake *p = head; p; p = p->next) { solidrectangle(p->x * CELL_SIZE, p->y * CELL_SIZE, (p->x + 1) * CELL_SIZE, (p->y + 1) * CELL_SIZE); } } void moveSnake() { int x = head->x, y = head->y; switch (direction) { case UP: y--; break; case DOWN: y++; break; case LEFT: x--; break; case RIGHT: x++; break; } Snake *p = new Snake; p->x = x; p->y = y; p->next = head; head = p; if (x < 0 || x > COLS || y < 0 || y > ROWS) { // 撞墙 closegraph(); printf("Game Over!\n"); exit(0); } for (Snake *p = head->next; p; p = p->next) { // 撞自己 if (p->x == head->x && p->y == head->y) { closegraph(); printf("Game Over!\n"); exit(0); } } if (x == food_x && y == food_y) { // 吃到食物 score += 10; food_x = rand() % COLS; food_y = rand() % ROWS; } else { Snake *p = head; while (p->next->next) { p = p->next; } delete p->next; p->next = NULL; } } void drawFood() { setfillcolor(RED); solidcircle(food_x * CELL_SIZE + CELL_SIZE / 2, food_y * CELL_SIZE + CELL_SIZE / 2, CELL_SIZE / 2); } void updateScore() { setbkmode(TRANSPARENT); settextcolor(WHITE); char buf[32]; sprintf(buf, "Score: %d", score); outtextxy(10, 10, buf); } int food_x, food_y; int main() { initgraph(WIDTH, HEIGHT); setbkcolor(DARKGRAY); initSnake(); food_x = rand() % COLS; food_y = rand() % ROWS; while (true) { cleardevice(); drawSnake(); drawFood(); updateScore(); moveSnake(); Sleep(100); if (_kbhit()) { switch (_getch()) { case 'W': case 'w': if (direction != DOWN) direction = UP; break; case 'S': case 's': if (direction != UP) direction = DOWN; break; case 'A': case 'a': if (direction != RIGHT) direction = LEFT; break; case 'D': case 'd': if (direction != LEFT) direction = RIGHT; break; } } } return 0; } ``` 在这个示例代码中,我们使用EasyX 图形库实现游戏窗口和绘制图形。我们使用 Snake 结构体来表示蛇身,其中的 next 指针指向下一个蛇身。当蛇移动时,我们将一个新的 Snake 节点插入到蛇头位置,然后删除蛇尾节点,从而实现蛇的移动。当蛇头碰到墙壁或者自己的身体时,游戏结束。当蛇头碰到食物时,得分加 10 分,并在随机位置生成一个新的食物。 在游戏循环中,我们使用 cleardevice() 函数清空屏幕,然后分别绘制蛇身、食物和得分。我们使用 Sleep(100) 函数控制游戏的帧率,从而实现动画效果。我们使用 _kbhit() 和 _getch() 函数来读取键盘输入,从而控制蛇的方向。
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pp不会算法^v^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值