Qt超简单实现贪吃蛇

文章目录

  • 常量
  • Snake类
  • GameController类
  • GUI显示
  • 游戏简图

为了能够最简单地完成程序,所以没有用类的继承等知识。感兴趣的朋友可以改写一下。

常量

const int FILE_SIZE = 30; //地图方格大小
const int FPS = 5000 / 33; //游戏运行帧率
enum Item{empty, wall, food, snake}; //方格的类型
//地图大小
const int mapWidth = 20;
const int mapHeight = 20;

Snake类

为了存储一条蛇, 我们需要的变量有:

  • 蛇的头坐标
  • 蛇的尾巴, 用一个链表存储
  • 蛇的移动方向

下面是Snake类的定义:

class Snake
{
public:Snake(QObject* parent, iItem ** _map, int x, int y);void setDirection(const int x, const int y);void move();void grow();bool eatSelf();int length()const{return tail.size();}QPoint getPos()const{return head;}private:bool isMoving();QPoint head;QList<QPoint> tail;int moveX = 0;int moveY = 0;iItem **map;
};

贪吃蛇中的唯一难点, 就是蛇移动的逻辑了. 蛇移动不必更新身体每一个点的位置. 相反, 只要移动头, 并把头原来的位置加入身子, 并且去掉身体的最后就可以了.

函数的实现:

Snake::Snake(QObject *parent, iItem **_map, int x, int y) : Item(parent), map(_map)
{head = QPoint(x, y);map[x][y] = snake;
}void Snake::setDirection(const int x, const int y)
{moveX = x;moveY = y;
}void Snake::move()
{if(!isMoving()) return;QPoint newHead = head;//更新head的位置newHead.setX((newHead.x() + moveX) % mapWidth);newHead.setY((newHead.y() + moveY) % mapHeight);if(!tail.empty()){//如果有尾巴的话, 将原来head作为tail的头tail.push_front(head);//剁掉尾巴的最后QPoint t = tail.back();map[t.x()][t.y()] = empty;tail.pop_back();}else{//没有尾巴, 则把原来的地图位置置空map[head.x()][head.y()] = empty;}head = newHead; //更新headmap[head.x()][head.y()] = snake; //更新地图
}bool Snake::isMoving()
{return moveX != 0 || moveY != 0;
}bool Snake::eatSelf()
{// 如果tail链表中包含与head坐标一样的点, 说明蛇头撞到身子了// contains是QList类内置的函数return tail.contains(head);
}void Snake::grow() //变长
{QPoint t = head;//尾巴变长的方向和移动方向相反t.setX((t.x() - moveX) % mapWidth);t.setY((t.y() - moveY) % mapHeight);tail.append(t);map[t.x()][t.y()] = snake;
}

GameController类

class GameController : public QObject
{Q_OBJECT
public:Item** map; GameController(QObject *parent);~GameController();protected://这个函数由于把QMainWindow收到的键盘事件在handlerKeyPressed里处理virtual bool eventFilter(QObject *watched, QEvent *event) override;void handleKeyPressed(QKeyEvent* event);private:QTimer timer;bool paused = false; //游戏暂停Snake *snake;QPoint foodPos;void checkCollision();bool snakeEatFood();void snakeEatSelf();void snakeHitWall();void addFood();void initMap(); //初始化地图signals:void updateView(); //通知更新显示void over(); //通知游戏终止public slots:void updateGame(); //更新游戏状态void gameover(); //游戏结束void stop(); //游戏暂停void resume(); //游戏恢复进行void newGame(); //再开一局
};
void GameController::initMap()
{for(int i = 0; i < mapWidth; ++i){for(int j = 0; j < mapHeight; ++j){map[i][j] = empty;}}
}GameController::GameController(QObject *parent) : QObject(parent)
{//创建动态数组map = new Item*[mapWidth];for(int i = 0; i < mapWidth; ++i){map[i] = new iItem[mapHeight];}initMap(); //初始化地图//连接信号connect(&timer, &QTimer::timeout, this, &GameController::updateGame);timer.start(FPS); //开始计时//蛇初始化到地图中央snake = new Snake(this, map, mapWidth/2, mapHeight/2);addFood(); //生成食物
}GameController::~GameController()
{for(int i = 0; i < mapWidth; ++i){delete []map[i];}delete []map;delete snake;
}bool GameController::eventFilter(QObject *watched, QEvent *event)
{if (event->type() == QEvent::KeyPress) {handleKeyPressed((QKeyEvent *)event); //自定义的按键处理函数return true; //返回已处理} else {return QObject::eventFilter(watched, event); //不处理}
}void GameController::handleKeyPressed(QKeyEvent *event)
{if(paused){ if(event->key() == Qt::Key_Space){resume();}return;}//设置方向if(event->key() == Qt::Key_Up){snake->setDirection(0, -1);}else if(event->key() == Qt::Key_Down){snake->setDirection(0, 1);}else if(event->key() == Qt::Key_Left){snake->setDirection(-1, 0);}else if(event->key() == Qt::Key_Right){snake->setDirection(1, 0);}else if(event->key() == Qt::Key_Space){stop();}
}void GameController::updateGame()
{snake->move(); //蛇移动checkCollision(); //检查碰撞emit updateView(); //更新显示
}void GameController::checkCollision()
{if(snake->eatSelf()){ // head撞到身体gameover();}if(snakeEatFood()){ //吃食物addFood(); //生成下一个食物snake->grow(); //蛇生长}
}void GameController::addFood()
{int n, x, y;do{ //随机在不是蛇的地方生成食物n = rand() % (mapWidth * mapHeight);y = n / mapWidth;x = n % mapWidth;}while(map[x][y] == snake);map[x][y] = food; //更新地图foodPos = QPoint(x, y); //存储食物的位置
}void GameController::gameover()
{stop(); //游戏暂停emit over(); //更新UI
}void GameController::stop()
{timer.stop();paused = true;
}void GameController::resume()
{timer.start(FPS);paused = false;
}void GameController::newGame()
{delete snake;initMap();snake = new Snake(this, map, mapWidth/2, mapHeight/2);addFood();resume();
}bool GameController::snakeEatFood()
{return snake->getPos() == foodPos;
}

GUI显示

class MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();protected:virtual void paintEvent(QPaintEvent* event); //绘制地图private:GameController *game;public slots:void showMessageBox(); //死亡时显示的提示框void updateView(); //更新游戏显示
};
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{setFixedSize(FILE_SIZE * mapWidth, FILE_SIZE * mapHeight); //设置窗口大小game = new GameController(this); //新开始游戏this->installEventFilter(game); //对收到的事件不接收,让game接收//信号连接connect(game, &GameController::updateView, this, &MainWindow::updateView);connect(game, &GameController::over, this, &MainWindow::showMessageBox);
}MainWindow::~MainWindow()
{delete game;
}void MainWindow::paintEvent(QPaintEvent *event)
{QPainter painter(this);//画背景painter.fillRect(0, 0, mapWidth * FILE_SIZE, mapHeight * FILE_SIZE, Qt::gray);//画方格内的物体//蛇,食物,墙分别用不同颜色表示for(int i = 0; i < mapWidth; ++i){for(int j = 0; j < mapHeight; ++j){switch (game->map[i][j]) {case empty:break;case food:painter.fillRect(i * FILE_SIZE, j * FILE_SIZE, FILE_SIZE, FILE_SIZE, Qt::red);break;case snake:painter.fillRect(i * FILE_SIZE, j * FILE_SIZE, FILE_SIZE, FILE_SIZE, Qt::yellow);break;case wall:break;}}}
}void MainWindow::showMessageBox()
{if (QMessageBox::Yes == QMessageBox::information(NULL,tr("Game Over"), tr("Again?"),QMessageBox::Yes | QMessageBox::No,QMessageBox::Yes)) {game->newGame(); //如果选了Yes则重新开始}else{exit(0); //否则推出}
}void MainWindow::updateView()
{update(); //更新, 会自动调用paintEvent函数
}

游戏简图

屏幕截图 2024-01-16 142451.png
屏幕截图 2024-01-16 142513.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/630063.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Netty通信中的粘包半包问题(三)

之前我们介绍了用特殊分隔符来分割每个报文&#xff0c;但是如果传输的数据中恰好有个特殊分隔符&#xff0c;它将会被拆分成多个&#xff0c;于是&#xff0c;为了进一步避免这个问题&#xff0c;还有一种解决方案是在两端的channelPipeline中用一个固定长度来区分&#xff0c…

K8s(一)Pod资源——Pod介绍、创建Pod、Pod简单资源配额

目录 Pod概述 pod网络 pod存储 pod和容器对比 创建pod的方式 pod运行方式分类 Pod的创建 Pod的创建过程 通过kubectl run来创建pod 通过yaml文件创建&#xff0c;yaml文件简单写法 Pod简单操作 Pod的标签labels Pod的资源配额resource 测试 Pod概述 Kubernetes …

贪心算法-活动安排-最详细注释解析

贪心算法-活动安排-最详细注释解析 题目&#xff1a; 学校在最近几天有n个活动&#xff0c;这些活动都需要使用学校的大礼堂&#xff0c;在同一时间&#xff0c;礼堂只能被一个活动使用。由于有些活动时间上有冲突&#xff0c;学校办公室人员只好让一些活动放弃使用礼堂而使用…

Arm Generic Interrupt Controller v3 and v4(GICv3v4)学习(一)

提示 该博客主要为个人学习&#xff0c;通过阅读官网手册整理而来&#xff08;个人觉得阅读官网的英文文档非常有助于理解各个IP特性&#xff09;。若有不对之处请参考参考文档&#xff0c;以官网参考文档为准。 Arm Generic Interrupt Controller v3 and v4学习一共分为三章&…

Revealing the Dark Secrets of MIM

论文名称&#xff1a; Revealing the Dark Secrets of Masked Image Modeling 发表时间&#xff1a;CVPR2022 作者及组织&#xff1a;Zhenda Xie, Zigang Geng, Hu Han等&#xff0c;来自清华&#xff0c;中科院&#xff0c;微软亚洲研究院。 前言 本文尝试探讨MIM为何有效的原…

JavaScript 学习笔记(Day5)

「写在前面」 本文为 b 站黑马程序员 pink 老师 JavaScript 教程的学习笔记。本着自己学习、分享他人的态度&#xff0c;分享学习笔记&#xff0c;希望能对大家有所帮助。推荐先按顺序阅读往期内容&#xff1a; 1. JavaScript 学习笔记&#xff08;Day1&#xff09; 2. JavaSc…

数据结构奇妙旅程之二叉树初阶

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN …

IDEA无法解析jdk自带的类的解决办法

1.问题 IDEA在有些时候&#xff0c;会出现这个错误&#xff0c;就是jdk自带的java类找不到而报错。 例如下面的就是Object找不到&#xff0c;我们知道jdk自带的类是不用import包的&#xff0c;这里报错了&#xff0c;IDEA会提示你去导入类&#xff0c;其实这个提示也不是真正…

Ubuntu 22.04 基础环境搭建

这是Ubuntu软件安装系列的第一篇&#xff0c;我们来聊聊基础环境搭建。 这个专栏主要讲一些常见服务端软件的安装和配置&#xff0c;当然也包括对软件架构和作用的分析&#xff0c;以及使用的场景的介绍。 注意我们这里使用的Ubuntu的版本是22.04&#xff0c;基本上大厂的云服…

CentOS安装maxwell

CentOs安装maxwell 一、简介二、准备工作三、安装1、下载安装包2、解压3、编写配置文件4、启动maxwell5、验证6、停止maxwell 四、说明1、更新数据2、插入数据3、删除数据 五、遇到问题 一、简介 maxwell是由美国Zendesk公司开源&#xff0c;它通过读取mysql的binlog日志&#…

vscode mysql cmake windows 常见问题和推荐文章

1.在windows中安装mingw64和cmake&#xff08;可查一下网上的安装教程&#xff09;&#xff0c;配置环境变量 2.在vscode中用CMake构建项目的时候&#xff0c;可能会出现这样的问题:“The C compiler identification is unknownn...”,可参考这篇博客 在windows下使用Vscode用…

面试题之ElasticSearch

面试题之ElasticSearch 1.es的基础知识2. es的集群、节点、分片、副本分片的定义&#xff1f;3. es为什么快&#xff1f;4. 倒排索引的原理是什么&#xff1f;5. es的segment是什么&#xff1f;6. es的分段存储和分段索引的概念及区别&#xff1f;7. 索引相关的问题&#xff1f…

服务器感染了.DevicData-P-XXXXXXXX勒索病毒,如何确保数据文件完整恢复?

引言&#xff1a; 在当今数字化时代&#xff0c;勒索病毒已成为网络安全威胁的一个严峻问题。其中&#xff0c;.DevicData-P-XXXXXXXX 勒索病毒以其恶意加密文件的手段引起了广泛关注。本文将介绍该病毒的特点、数据恢复方法以及如何预防遭受其攻击。 如不幸感染这个勒索病毒&…

【Java数据结构】03-二叉树,树和森林

4 二叉树、树和森林 重点章节&#xff0c;在选择&#xff0c;填空&#xff0c;综合中都有考察到。 4.1 掌握二叉树、树和森林的定义以及它们之间的异同点 1. 二叉树&#xff08;Binary Tree&#xff09; 定义&#xff1a; 二叉树是一种特殊的树结构&#xff0c;其中每个节点…

Python(wordcloud):根据词频信息(xlsx、csv文件)绘制词云图

一、前言 本文将介绍如何用python根据词频信息&#xff08;xlsx、csv文件&#xff09;绘制词云图&#xff0c;除了绘制常规形状的词云图&#xff08;比如长方形&#xff09;&#xff0c;还可以指定词云图的形状。 二、安装并引入相关的库 1、安装相关的库 pip install jieb…

MinIO上传

简介 MinIO是一个开源的对象存储服务器&#xff0c;兼容Amazon S3 API。它提供了可扩展、高性能和高可用性的存储解决方案&#xff0c;适用于大规模数据存储和分发。 MinIO的设计目标是简单易用、轻量级和高度可定制。它可以在企业内部部署&#xff0c;也可以在公有云上使用。…

深度学习笔记(八)——构建网络的常用辅助增强方法:数据增强扩充、断点续训、可视化和部署预测

文中程序以Tensorflow-2.6.0为例 部分概念包含笔者个人理解&#xff0c;如有遗漏或错误&#xff0c;欢迎评论或私信指正。 截图和程序部分引用自北京大学机器学习公开课 要构建一个完善可用的神经网络&#xff0c;除了设计网络结构以外&#xff0c;还需要添加一些辅助代码来增强…

【Macos系统】安装VOSviewer及使用VOSviewer教程!!以ESN网络的研究进行案例分析

【Macos系统】安装VOSviewer及使用VOSviewer教程 以ESN网络的研究进行案例分析 本文介绍如何安装和使用VOSviewer软件&#xff0c;并以ESN&#xff08;Echo State Network&#xff09;网络的研究为案例进行分析。利用VOSviewer对相关文献进行可视化分析&#xff0c;并深入了解…

Linux之引导和服务篇

系统引导是操作系统运行的开始&#xff0c;在用户能够正常登录之前&#xff0c;Linux的引导过程完成了一系列的初始化任务&#xff0c;并加载必要的程序和命令终端&#xff0c;为用户登录做好准备。 一. 引导过程 开机自检--->MBR引导--->GRUB菜单--->加载Linux内核-…

商汤书生大模型一次可读 30 万汉字;2023 年 Shopee Live 超100万马来人注册;2023年中国出生人口902万人;

今日精选 • 商汤“书生・浦语”2.0 大语言模型开源&#xff1a;200K 上下文&#xff0c;一次可读 30 万汉字• 2023年中国出生人口902万人• 2023 年 Shopee Live 有超 100 万马来人注册并观看直播 投融资 • Airbnb 2 亿美元收购人工智能初创公司 Gameplanner.AI• 哥伦比…