本项目资源文件借鉴了此篇文章内容,https://blog.csdn.net/weixin_42756970/article/details/108975622python版本的雷电小游戏。
前言
通过Qt做一个UI界面控制飞机上下左右移动的小实验,通过按下WSAD四个键盘按键,实现飞机的移动,并且在飞机向上移动的状态时,添加飞机尾焰的视觉效果。
本实验的主要思路是通过在GUI界面类Widget类中,重写keyPressEvent、keyReleaseEvent进行相关的判断操作,并且重写paintEvent,设定一个定时器,到设定的时间时,不断地调用update()函数进行图形界面的重绘。
效果
实验效果如下图所示,通过键盘WSAD来控制红色小飞机的移动。
资源下载
本次实验全部代码请至https://download.csdn.net/download/wang_chao118/86427796下载。
核心代码
分开定义飞机类heroPlane以及GUI界面类Widget。
heroplane.h
#ifndef HEROPLANE_H
#define HEROPLANE_H
#include <QPixmap>
class heroPlane
{
public:
heroPlane();
//飞机原始图片
QPixmap plane_pic;
//向左移动时的飞机图片
QPixmap plane_pic_moveleft;
//向右移动时的飞机图片
QPixmap plane_pic_moveright;
//尾焰图片
QPixmap air;
//飞机当前坐标
int m_X;
int m_Y;
};
#endif // HEROPLANE_H
heroplane.cpp
#include "heroplane.h"
#include "config.h"
heroPlane::heroPlane()
{
//加载飞机图片
plane_pic.load(":/hero.png");
plane_pic_moveleft = plane_pic.copy(198,0,318-255,87);
plane_pic_moveright = plane_pic.copy(53,0,318-255,87);
plane_pic = plane_pic.copy(120, 0, 318 - 242, 87);
air.load(":/air.png");
air = air.copy(80, 0, 55, 65);
//初始化坐标
m_X = GAME_WIDTH * 0.5 - plane_pic.width() *0.5;
m_Y = GAME_HEIGHT - plane_pic.height();
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QKeyEvent>
#include <QDebug>
#include <QPainter>
#include <QTimer>
#include "heroplane.h"
#include "config.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void keyPressEvent(QKeyEvent * event);
void keyReleaseEvent(QKeyEvent *event);
void paintEvent(QPaintEvent *event);
void judgekey();
private:
Ui::Widget *ui;
heroPlane *hero;
QTimer *timer;
//按键标志位,用于判断是否有按键按下
bool keypressflag;
//按键种类标志,用于判断是WSAD中的哪个
int keyflag;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
setFixedSize(GAME_WIDTH, GAME_HEIGHT);
keypressflag = false;
//实例化一个飞机对象
hero = new heroPlane;
//设置重绘速率,此处为每5ms重绘一次界面
timer = new QTimer(this);
//每次到时间就调用update()函数,update()函数将调用paintEvent()
connect(timer,SIGNAL(timeout()),this,SLOT(update()));
timer->start(5);
}
Widget::~Widget()
{
delete ui;
}
void Widget::keyPressEvent(QKeyEvent *event)
{
qDebug()<<event->key();
//判断按键种类
switch(event->key())
{
case Qt::Key_W:
//有按键按下即将按键标志位置1
keypressflag = true;
//设置按键种类标志位
keyflag = 1;
return;
case Qt::Key_S:
keypressflag = true;
keyflag = 2;
return;
case Qt::Key_A:
keypressflag = true;
keyflag = 3;
return;
case Qt::Key_D:
keypressflag = true;
keyflag = 4;
return;
}
}
void Widget::keyReleaseEvent(QKeyEvent *event)
{
switch(event->key())
{
//有按键松开时即将按键标志位置0
case Qt::Key_W:
keypressflag = false;
return;
case Qt::Key_S:
keypressflag = false;
return;
case Qt::Key_A:
keypressflag = false;
return;
case Qt::Key_D:
keypressflag = false;
return;
}
}
//界面绘制事件
void Widget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
if(keypressflag)
{
//判断是哪个键按下了,绘制对应的图片
judgekey();
}
else
{
QPainter painter(this);
painter.drawPixmap(hero->m_X,hero->m_Y,hero->plane_pic);
}
// qDebug()<<"keypressflag: "<<keypressflag;
}
void Widget::judgekey()
{
QPainter painter(this);
switch(keyflag)
{
case 1:
//设置飞机的坐标值,绘制向上移动的飞机
hero->m_Y = (hero->m_Y - 1 < 0) ? hero->m_Y : hero->m_Y - 1;
painter.drawPixmap(hero->m_X,hero->m_Y,hero->plane_pic);
//绘制尾焰图形
painter.drawPixmap(hero->m_X+8,hero->m_Y + 80,hero->air);
return;
case 2:
hero->m_Y = (hero->m_Y + 1 + hero->plane_pic.height() > GAME_HEIGHT) ? hero->m_Y : hero->m_Y + 1;
painter.drawPixmap(hero->m_X,hero->m_Y,hero->plane_pic);
return;
case 3:
hero->m_X = (hero->m_X - 1 < 0) ? hero->m_X : hero->m_X - 1;
painter.drawPixmap(hero->m_X,hero->m_Y,hero->plane_pic_moveleft);
return;
case 4:
hero->m_X = (hero->m_X + 1 + hero->plane_pic.width() > GAME_WIDTH) ? hero->m_X : hero->m_X + 1;
painter.drawPixmap(hero->m_X,hero->m_Y,hero->plane_pic_moveright);
return;
}
}
资源下载
本次实验全部代码请至https://download.csdn.net/download/wang_chao118/86427796下载。
遗留问题
Qt按键事件中的相应问题:
(1)Qt按键在长按状态下会反复调用keyPressEvent以及keyReleaseEvent,且如果一直在长按状态下时,第一次的keyPressEvent与第二次的keyPressEvent之间有一定间隔时间,导致的现象就是,如果直接在keyPressEvent函数中直接设定飞机的位置的话,将会产生一种“停顿”的现象,为了避免这种现象,本案例在Widget类中引入了一个keypressflag来判断按键是否按下,只要有按键按下就开始重绘界面,直到有keyReleaseEvent后停止重绘界面,解决了“停顿”现象。
(2)本案例稍有缺陷,如果同时按下多个按键,即同时按下W及A按键、按下多个按键,然后再松开某个按键时,飞机的运行状态不明确,或产生不运动的现象。