200+行代码写一个简易的Qt界面贪吃蛇
照例先演示一下:
一个简单的Qt贪吃蛇,所有的图片都是我自己画的(得意)。
大致的运行逻辑和之前那个200行写一个C++小黑窗贪吃蛇差不多,因此在写这个项目的时候,大多情况是在想怎么通过Qt给展现出来。
背景图片:
由于背景图片是自己画的,因此大小是刚刚好符合我设定的主窗口大小的,直接是把背景图放大两倍,然后放在坐标为0,0的位置上,下面这段代码写在绘画事件的函数里,窗口初始化的时候系统会自动调用绘画事件,因此我们不用手动去调用就可以实现背景图片的渲染。
QPainter* paint = new QPainter(this);//画背景,可以修改为可选背景,感兴趣可以自己去搞一下QPixmap background;background.load(":/image/background.png");background=background.scaled(background.width()*2, background.height()*2);paint->drawPixmap(0,0,background);
那么除了背景图片以外,还有要画的就是蛇和食物了(这里的食物我画了我最喜欢的水果——芭乐,应该看得出来叭),所以在主窗口里需要有对应的蛇和食物的类,并且这些类里需要有它们所在位置的坐标,然后再绘画事件里根据它们的坐标来绘制。
画食物比较简单,直接在食物的坐标上绘制就行。
有界面的贪吃蛇和小黑窗不一样,我们需要展示出蛇的移动方向,也就是说不同的移动方向,蛇头 应该看起来是不一样的,所以在绘画蛇头的时候我们需要判断蛇的移动方向,以此来决定蛇头应该用哪个图片。(直接旋转图片有些麻烦,所以我直接画了四个方向的蛇头图片)。
蛇身子应该也是要根据移动的情况而绘制不同的图片的,但是逻辑太复杂了,不仅要考虑蛇的每一节身子是上下方向的还是左右方向的,甚至有些身子是弯曲的转向的,所以我直接投降了,把蛇的身子画成圆乎乎的,这样就分辨不出什么方向什么的了,统一用同一个图片就好啦。
蛇尾和蛇头类型,也是要根据具体情况来绘制不同的图片,不过和蛇头不一样的是,蛇尾的图片不取决与蛇的移动方向,因为蛇尾具有滞后性,所以判断的依据是蛇的最后一节身子。
if (isBegin) { //当游戏开始时才画蛇和食物//画食物QPixmap bale;bale.load(":/image/bale.png");paint->drawPixmap(food[0],food[1],bale);//画蛇头QPixmap snakeHead;//根据蛇移动的方位来改变蛇头的图片if(greedysnake.direction=='r')snakeHead.load(":/image/head(right).png"); else if(greedysnake.direction=='l')snakeHead.load(":/image/head(left).png");else if(greedysnake.direction=='u')snakeHead.load(":/image/head(up).png");else snakeHead.load(":/image/head(down).png");paint->drawPixmap(greedysnake.head[0], greedysnake.head[1], snakeHead);//画蛇身for (auto& body : greedysnake.body) {QPixmap snakeBody;snakeBody.load(":/image/body.png");paint->drawPixmap(body[0], body[1], snakeBody);}//画蛇尾QPixmap snakeTail;vector<int>lastbody = *(greedysnake.body.end() - 1);//根据蛇的最后一节身子来改变蛇尾的图片if (greedysnake.tail[0] == lastbody[0]) {if (greedysnake.tail[1] > lastbody[1]) snakeTail.load(":/image/tail(up).png");else snakeTail.load(":/image/tail(down).png");}else if (greedysnake.tail[1] == lastbody[1]) {if (greedysnake.tail[0] > lastbody[0]) snakeTail.load(":/image/tail(left).png");else snakeTail.load(":/image/tail(right).png");}paint->drawPixmap(greedysnake.tail[0],greedysnake.tail[1],snakeTail);}
生成食物:
生成食物的时间点在游戏刚开始的时候,以及蛇吃掉食物以后。
生成食物其实就是更新食物的坐标,我们使用随机数去生成,并且我这边设置的是食物大小是50*50像素的,并且蛇的每一节身子都是50*50像素的,所以我们在生成坐标的时候,需要保证生成出来的坐标是50的倍数(包括蛇自身的坐标)。
并且生成出来的坐标不能在蛇身上。
void MainUI::createFood(){//生成食物std::uniform_int_distribution<int>uw(0,1300/50-1);std::uniform_int_distribution<int>uh(1,800/50-1);bool flag = true;while (1) {flag = true;int x = uw(e)*50; int y = uh(e)*50;//不能生成在蛇头的位置if (x == greedysnake.head[0] && y == greedysnake.head[1]) continue;//不能生成在蛇尾的位置if (x == greedysnake.tail[0] && y == greedysnake.tail[1]) continue;//不能生成在蛇身的位置for (const auto &body : greedysnake.body) {if (body[0] == x && body[1] == y) {flag = false;break;}}if (flag) {food[0] = x, food[1] = y;qDebug() << x << ' ' << y << endl;break;}}
}
让蛇动起来:
Qt有定时器,这是对比小黑窗来说比较方便的一个点,我们可以通过定时器,每个一段时间就更新蛇的坐标,然后再更新绘图,以此来达到让蛇移动的效果。
每次移动蛇,我们就把蛇头的坐标按照移动的方向来做出改变。而蛇的身子要达到移动的效果则是把最后一节身子的坐标从身子里删去,然后在存放身子的容器的开头加上移动前蛇头的坐标,这样蛇的身子也就可以达到移动的效果了。
而蛇的尾部只需要更新成移动前蛇的最后一节身子的坐标即可。
在移动的时候我们还应该有个判断,如果吃到了芭乐,即新蛇头的坐标等于芭乐的坐标,那么身子加长,并且重新生成新的芭乐。加长身子是将移动前头的坐标加入到蛇身子的容器的开头,而芭乐的坐标成为新的蛇头的坐标,这样就达到了增长的效果。
除了判断食物,我们还应该判断如果吃到了自己的身体,我们就应该弹出一个消息提示框,可以选择是否重新开始一局游戏。
如果蛇移动移出了边界,那么可以判断是失败了,而我这里的处理是可以穿越到界面的另一侧继续游戏,这样降低了难度,增加了可玩性。
void MainUI::timerEvent(QTimerEvent* e){ //定时器if (isBegin) { //如果开始游戏//蛇的移动greedysnake.body.insert(greedysnake.body.begin(), greedysnake.head);greedysnake.tail = *(greedysnake.body.end() - 1);greedysnake.body.pop_back();if (greedysnake.direction == 'r') greedysnake.head[0] += greedysnake.speed;else if (greedysnake.direction == 'l') greedysnake.head[0] -= greedysnake.speed;else if (greedysnake.direction == 'u') greedysnake.head[1] -= greedysnake.speed;else greedysnake.head[1] += greedysnake.speed;for (auto& body : greedysnake.body) {//如果碰到身体if (body[0] == greedysnake.head[0] && body[1] == greedysnake.head[1]) {isBegin = false;killTimer(TimerID);QMessageBox::StandardButton ans = QMessageBox::question(this, "game over", QString::fromLocal8Bit("你吃到了自己,游戏结束,是否在来一把"));if (ans == QMessageBox::No) {exit(0);}else {//手动初始化一下蛇greedysnake.head = { 650,350 };greedysnake.body = {{ 600, 350 }};greedysnake.tail = { 550,350 };beginGame();}return;}}if (greedysnake.head[0] == food[0] && greedysnake.head[1] == food[1]) {//如果吃到食物,身体变长并且重新生成食物.greedysnake.body.insert(greedysnake.body.begin(), greedysnake.head);if (greedysnake.direction == 'r') greedysnake.head[0] += greedysnake.speed;else if (greedysnake.direction == 'l') greedysnake.head[0] -= greedysnake.speed;else if (greedysnake.direction == 'u') greedysnake.head[1] -= greedysnake.speed;else greedysnake.head[1] += greedysnake.speed;//增加难度,加快速度,不要也可以if (greedysnake.body.size() > 5) { killTimer(TimerID);TimerID = startTimer(200); //通过改变调用定时器的频率来达到改变蛇的速度}else if (greedysnake.body.size() > 10) {killTimer(TimerID);TimerID = startTimer(180);}else if (greedysnake.body.size() > 15) {killTimer(TimerID);TimerID = startTimer(150);}else if (greedysnake.body.size() > 20) {killTimer(TimerID);TimerID = startTimer(120);}else if (greedysnake.body.size() > 25) {killTimer(TimerID);TimerID = startTimer(100);}createFood();}//全屏移动,即可以从下面穿过,到达上面if (greedysnake.head[0] < 0) greedysnake.head[0] = 1250;if (greedysnake.head[0] > 1300)greedysnake.head[0] = 0;if (greedysnake.head[1] >= 800) greedysnake.head[1] = 50;if (greedysnake.head[1] <= 0) greedysnake.head[1] = 800;update(); //更新绘图}
}
按键事件:
主要的核心功能就在上面了,我这里最后补充一个按键操控蛇的点。
我们可以通过重写窗口的按键事件来获取到玩家按下的按钮。
通过e->key()来获取一个整型数据,每个按钮都对应一个数值,我一个个把按键试出来了,然后写了个逻辑,如果在一开始的时候,我们除了可以通过鼠标点击来开始游戏,也可以通过按下回车键来开始,另外也可以按下空格来开始游戏,但是实际上我没有写关于空格的逻辑,但是它就是可以通过空格来触发那个按钮。
另外在游戏中可以按下空格来暂停游戏。
值得一提的是在改变蛇的移动方向的时候,需要注意不能直接180°转弯。
void MainUI::keyPressEvent(QKeyEvent* e){int nowKey = e->key();if (nowKey == 16777220) { //回车键if(begin!=nullptr) begin->click();}if (isBegin) {//修改运动方向,注意不能直接180°转弯if ((nowKey == 87 || nowKey == 16777235) && greedysnake.direction != 'd') greedysnake.direction = 'u';else if ((nowKey == 83 || nowKey == 16777237) && greedysnake.direction != 'u') greedysnake.direction = 'd';else if ((nowKey == 65 || nowKey == 16777234) && greedysnake.direction != 'r') greedysnake.direction = 'l';else if ((nowKey == 68 || nowKey == 16777236) && greedysnake.direction != 'l')greedysnake.direction = 'r';else if (nowKey == 32) isBegin=false;}else {if (nowKey == 32) isBegin = true;}
}
小结:
我的评价是不管是学习什么技术,最好的练手项目就是写一个贪吃蛇,之前学C,java,python,C++的时候都写了贪吃蛇,而现在学了qt也写了个贪吃蛇,这对于巩固基础有着非常好的效果。
想要源码的小伙伴可以直接在我CSDN的主页里找到对应的资源免费下载,我已经上传到SCDN了,也可以关注我的微信公众号:折途想要敲代码,回复关键词“qt贪吃蛇”领取完整的源码。
相关文章:
200+行代码写一个简易的Qt界面贪吃蛇
照例先演示一下: 一个简单的Qt贪吃蛇,所有的图片都是我自己画的(得意)。 大致的运行逻辑和之前那个200行写一个C小黑窗贪吃蛇差不多,因此在写这个项目的时候,大多情况是在想怎么通过Qt给展现出来。 背景图…...
redis中使用bloomfilter的白名单功能解决缓存穿透问题
一 缓存预热 1.1 缓存预热 将需要的数据提前缓存到缓存redis中,可以在服务启动时候,或者在使用前一天完成数据的同步等操作。保证后续能够正常使用。 1.2 缓存穿透 在redis中,查询redis缓存数据没有内容,接着查询mysql数据库&…...
Spring Boot 2.7.8以后mysql-connector-java与mysql-connector-j
错误信息 如果升级到Spring Boot 2.7.8,可以看到因为找不到mysql-connector-java依赖而出现错误。 配置: <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId>&l…...
03|「如何写好一个 Prompt」
前言 Prompt 文章目录 前言一、通用模板和范式1. 组成2. 要求1)文字描述2)注意标点符号 一、通用模板和范式 1. 组成 指令(角色) 生成主体 额外要求 指令:模型具体完成的任务描述。例如,翻译一段文字&…...
关于提示词 Prompt
Prompt原则 原则1 提供清晰明确的指示 注意在提示词中添加正确的分割符号 prompt """ 请给出下面文本的摘要: <你的文本> """可以指定输出格式,如:Json、HTML提示词中可以提供少量实例,…...
【Linux多线程】线程的互斥与同步(附抢票案例代码+讲解)
线程的互斥与同步 💫 概念引入⭐️临界资源(Critical Resource):🌟临界区(Critical Section):✨互斥(Mutex): ⚡️结合代码看互斥☄️ 代码逻辑&a…...
ajax概述
目录 1.什么是ajax 2.ja原生ajax 3.jQuery框架的ajax 4.综合案例 1.什么是ajax Ajax 即"Asynchronous Javascript And XML"(异步 JavaScript 和 XML),是指一种创建交互式网页应用的网页开发技术。Ajax 异步 JavaScript 和 XML&…...
小白带你学习linux的mysql服务(主从mysql服务和读写分离三十一)
目录 二、MySQL Replication优点: 三、MySQL复制类型 1、异步复制(Asynchronous repication) 2、全同步复制(Fully synchronous replication) 3、半同步复制(Semisynchronous replication)…...
【低代码专题方案】iPaaS运维方案,助力企业集成平台智能化高效运维
01 场景背景 随着IT行业的发展和各家企业IT建设的需要,信息系统移动化、社交化、大数据、系统互联、数据打通等需求不断增多,企业集成平台占据各个企业领域,成为各业务系统数据传输的中枢。 集成平台承接的业务系统越多,集成平台…...
Android SDK 上手指南||第一章 环境需求||第二章 IDE:Eclipse速览
第一章 环境需求 这是我们系列教程的第一篇,让我们来安装Android的开发环境并且把Android SDK运行起来! 介绍 欢迎来到Android SDK入门指南系列文章,如果你想开始开发Android App,这个系列将从头开始教你所须的技能。我们假定你…...
Amazon Linux上使用ec2-user来设置开机自启动的shell脚本
要在Amazon Linux上使用ec2-user来设置开机自启动的shell脚本,可以按照以下步骤操作: 1. 确保您拥有要设置自启动的shell脚本。假设脚本的路径是/home/ec2-user/myscript.sh。 2. 使用以下命令打开/etc/rc.d/rc.local文件: shell sudo nano /…...
【Spring】Spring 下载及其 jar 包
根据 【动力节点】最新Spring框架教程,全网首套Spring6教程,跟老杜从零学spring入门到高级 以及老杜的原版笔记 https://www.yuque.com/docs/share/866abad4-7106-45e7-afcd-245a733b073f?# 《Spring6》 进行整理, 文档密码:mg9b…...
蓝桥杯2023年第十四届省赛-飞机降落
题目描述 N 架飞机准备降落到某个只有一条跑道的机场。其中第 i 架飞机在 Ti 时刻到达机场上空,到达时它的剩余油料还可以继续盘旋 Di 个单位时间,即它最早 可以于 Ti 时刻开始降落,最晚可以于 Ti Di 时刻开始降落。降落过程需要 Li个单位时…...
STM32 串口实验(学习一)
本章将实现如下功能:STM32通过串口和上位机对话,STM32在收到上位机发过来的字符串后,原原本本返回给上位机。 STM32 串口简介 串口作为MCU的重要外部接口,同时也是软件开发重要的调试手段,其重要性不言而喻。现在基本…...
多臂治疗规则的 Qini 曲线(Stefan Wager)
英文题目: Qini Curves for Multi-Armed Treatment Rules 中文题目:多臂治疗规则的 Qini 曲线 单位:Stefan Wager 论文链接: 代码:GitHub - grf-labs/maq: Treatment rule evaluation via the multi-armed Qini …...
NOSQL之Redis配置及优化
目录 一、关系型数据库 二、非关系型数据库 三、关系型数据库和非关系型数据库区别 1、数据存储方式不同 2、扩展方式不同 3、对事务性的支持不同 四、Redis简介 五、Redis优点 (1)具有极高的数据读写速度 (2)支持丰富的…...
植物一区HR | 植物生理组+转录组:揭示豆科植物响应干旱胁迫机制
PlantArray 植物高通量生理学表型监测系统 是一套以植物生理学为基础的高精度,高通量,自动化表型监测系统,集合实验设置、数据分析、决策工具于一身,能够高通量实时动态监测并进行全天候生理及环境参数采集,是进行植物…...
TCP粘包问题
TCP粘包问题 TCP粘包问题造成TCP粘包的原因发送方原因接收方原因 如何处理TCP粘包发送方接收方应用层 为什么UDP没有粘包问题 TCP粘包问题 TCP粘包就是指发送方发送的若干包数据到达接收方时粘成了一包,从接收缓冲区来看,后一包数据的头紧接着前一包数据…...
QT【day1】
登录框: #include "mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent) {//窗口设置this->setFixedSize(600,600); //大小this->setWindowTitle("MUMU"); //文本内容this->setWindowOpacity(0.8); //透…...
【Golang】Golang进阶系列教程--为什么 Go 不支持 []T 转换为 []interface
文章目录 前言官方解释内存布局程序运行中的内存布局通用方法 前言 在 Go 中,如果 interface{} 作为函数参数的话,是可以传任意参数的,然后通过类型断言来转换。 举个例子: package mainimport "fmt"func foo(v inter…...
两数相加 II——力扣445
题目描述 法一 栈 本题旨在从后往前加,为了逆序处理所有数位,利用栈,把数字压入栈中,再依次取出相加,注意进位!进位是/10,另外需要注意栈的常用函数,push()、pop()、top()࿰…...
js获取上传视频的封面第一帧
代码如下:粘贴到这个在线编辑器里,可以测试效果。 菜鸟教程在线编辑器 <div><div style"flex: 1;border: 1px solid #999; position:relative;color: #333;background-color:#FFF2B8;"><span style"position: absolute…...
Nginx 高可用负载均衡(三种模式)
一、nginx普通集群负载均衡 1、安装keepalived (1)下载 https://www.keepalived.org/download.html(2)解压 tar -zxvf keepalived-2.0.18.tar.gz(3)使用configure命令配置安装目录与核心配置文件所在位置: ./configure --prefix/usr/local/keepalived --sysconf/e…...
Linux tail命令
在Linux中,tail命令用于查看文件的末尾内容。它可以显示文件的最后几行,默认情况下显示最后10行。 以下是一些常见的使用方式和示例: 显示文件的最后10行: tail filename将会显示名为filename的文件的最后10行内容。 显示文件…...
【屏幕适配发展介绍 Objective-C语言】
一、接下来,我们花一天时间,给大家介绍这个屏幕适配 1.那么,屏幕适配,是什么意思啊 我们说,写程序的时候,我们有时候要做 1)系统适配 2)屏幕适配 1)系统适配:是指的你写的这个代码,在iOS6、iOS7、iOS8,在不同的iOS系统下,是不是运行的效果,一致吧 这个指的是…...
linux中ls命令详解
ls 显示目录内容列表 补充说明 ls命令 就是list的缩写,用来显示目标列表,在Linux中是使用率较高的命令。ls命令的输出信息可以进行彩色加亮显示,以分区不同类型的文件。 语法 ls [选项] [文件名...][-1abcdfgiklmnopqrstuxABCDFGLNQRSUX…...
大盗阿福(记忆化搜索板子)
提供核心代码:(经典的记忆化搜索套路) int dfs(int pos){if(f[pos]!-1) return f[pos];//记忆化if(pos>n) return 0;//边界,越界int sum0;//模板int f10,f20;f1dfs(pos1);f2dfs(pos2)w[pos];summax(f1,f2);//模板f[pos]sum;//模…...
打卡力扣题目八
#左耳听风 ARST 打卡活动重启# 目录 一、问题 二、解题方法一 三、解题方法二 四、两种方法的区别 关于 ARTS 的释义 —— 每周完成一个 ARTS: ● Algorithm: 每周至少做一个 LeetCode 的算法题 ● Review: 阅读并点评至少一篇英文技术文章 ● Tips: 学习至少一…...
matlab使用教程(5)—矩阵定义和基本运算
本博客介绍如何在 MATLAB 中创建矩阵和执行基本矩阵计算。 MATLAB 环境使用矩阵来表示包含以二维网格排列的实数或复数的变量。更广泛而言,数组为向量、矩阵或更高维度的数值网格。MATLAB 中的所有数组都是矩形,在这种意义上沿任何维度的分量向量的长度…...
用HTML写一个简单的静态购物网站
实现代码: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>购物网站</title> &l…...
centos7.2 wordpress/北京营销型网站
2022/2/18 今天看到了一个很好的博客:https://wudan.blog.csdn.net/article/details/121909047?spm1001.2014.3001.5502 有关于promise的,我自己去尝试写接口试试 就是说resolve(subList) 那么res就是subList啊。没有res.subList vue 每隔几秒刷新请求…...
怎么自己做歌曲网站/石家庄抖音seo
来源:广东自然资源8月29日是第17个全国测绘法宣传日,广东省自然资源厅全面开展测绘法宣传日暨国家版图意识宣传周活动。通过一系列主题活动,宣传倡导正确使用地图,呼吁公众“规范使用地图 一点都不能错”(☞ 具体内容,…...
公司网站制作汇报会/汽车网络营销策划方案
Done 转载于:https://www.cnblogs.com/ChrisInsistPy/p/11341651.html...
档案馆网站建设/什么时候网络推广
https://blog.csdn.net/my_heart_/article/details/52601924 端口号的范围是从1~65535 端口的概念: 在网络技术中,端口(Port)大致有两种意思:一是物理意义上的端口,比如,ADSL Modem、…...
国内网站开发语言/seo优化论坛
IBM SPSS Statistics对个案值的计数功能,是通过构建新的个案计数变量,来统计每个个案的指定变量列表中设置值的出现次数。 对个案值的计数功能,既可以用于字符串变量的计数,也可以用于数值型变量的计数。接下来,我们将…...
乐清市规划图高清/天津seo网络营销
在$_REQUEST[where] $where;$storeQuery->process_views($currentModule);上面加入以下代码,下面的代码指默认为查询本月if($where){ $date_period thismonth; $date_from get_date_from($date_period); $date_to get_date_to($date_period); if(isset($date_from) &…...