Qt の小さなプロジェクト Snake Solid Line は、主にタイマー、シグナルとスロット、キーイベント、描画イベント、座標操作、乱数生成などをマスターします。
Qt スネークのデモ
QWidget 描画インターフェース
プロジェクトのソースファイルには明確にコメントが付けられています
ウィジェット.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QtDebug>
#include <QKeyEvent>
#include <QTimer>
#include <QRectF>
#include <QPointF>
#include <QIcon>
#include <QPainter>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget; }
QT_END_NAMESPACE
typedef enum direct {
dir_UP,
dir_DOWN,
dir_LEFT,
dir_RIGHT
}dir_t;
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
void topAddRect();
void deleteLast();
void downAddRect();
void leftAddRect();
void rightAddRect();
void addFood();
bool checkHit();
protected:
// 按键按压处理
void keyPressEvent(QKeyEvent *event);
void paintEvent(QPaintEvent *event);
private Q_SLOTS:
void my_timeout();
private:
Ui::Widget *ui;
int moveFlag = dir_t::dir_UP;
bool gameStart = false;
QTimer *timer;
const int startTime = 100;
// 贪吃蛇
QList<QRectF>snakeList;
// 贪吃蛇小方块
const int nodeWidth = 20;
const int nodeHeight = 20;
// 食物
QRectF food;
};
#endif // WIDGET_H
ウィジェット.cpp
#include "widget.h"
#include "ui_widget.h"
// 三个方块代码蛇,最上面的第一个和最后面的一个通过按键进行交替删除 即snake[0]永远表示头
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowTitle("贪吃蛇疯子");
this->setWindowIcon(QIcon(":/icons/title.jpg"));
qDebug()<<"x = "<<this->x()<<" y = "<<this->y()<<" height = "<<this->height()<<" width = "<<this->width();
timer = new QTimer();
connect(timer, &QTimer::timeout, this , &Widget::my_timeout);
// 初始化蛇身子 3个方块
QRectF rect(this->width()/2,this->height()/2,nodeWidth,nodeHeight);
snakeList.append(rect);
topAddRect();
topAddRect();
// 添加食物
addFood();
}
Widget::~Widget()
{
delete ui;
}
// 增加一个方块 左上和右下确认一个方块坐标系
void Widget::topAddRect()
{
QPointF leftTop; // 左上角坐标
QPointF rightBottom; // 右上角坐标
if (snakeList[0].y() <= 0) {
// 当蛇移动到窗口顶部:确认新坐标 y = 窗口高度 - 蛇方块高度
leftTop = QPointF(snakeList[0].x(), this->height() - nodeHeight);
rightBottom = QPointF(snakeList[0].x() + nodeWidth , this->height());
}
else {
// 向上移动: y坐标必然减少 减少的右下角坐标为之前的下一个方块的右上角坐标
leftTop = QPointF(snakeList[0].x(), snakeList[0].y() - nodeHeight);
rightBottom = snakeList[0].topRight();
}
// 插入矩形小方块1个,由于采用的是List链表,这是典型的前插,追加
snakeList.insert(0,QRectF(leftTop,rightBottom));
}
void Widget::downAddRect()
{
QPointF leftTop; // 左上角坐标
QPointF rightBottom; // 右上角坐标
if (snakeList[0].y() > this->height() - nodeHeight) {
// 当蛇移动到窗口底部:确认新坐标 y = 0
leftTop = QPointF(snakeList[0].x(), 0);
rightBottom = QPointF(snakeList[0].x() + nodeWidth, 0 + nodeHeight);
}
else {
// 向下移动: y坐标必然增加
leftTop = QPointF(snakeList[0].x(), snakeList[0].y() + nodeHeight);
rightBottom = snakeList[0].bottomRight() + QPointF(0,nodeHeight);
}
// 插入矩形小方块1个
snakeList.insert(0,QRectF(leftTop,rightBottom));
}
void Widget::leftAddRect()
{
QPointF leftTop; // 左上角坐标
QPointF rightBottom; // 右上角坐标
if (snakeList[0].x() <= 0) {
// 当蛇移动到窗口最左部:确认新坐标 x = 窗口宽度 - 小方块宽度
leftTop = QPointF(this->width() - nodeWidth, snakeList[0].y());
}
else {
// 向左移动:x坐标必然减少
leftTop = QPointF(snakeList[0].x() - nodeWidth, snakeList[0].y());
}
// 右下角坐标 = 之前一个的左上角坐标x,y + 小方块的宽高
rightBottom = leftTop + QPointF(nodeWidth, nodeHeight);
// 插入矩形小方块1个
snakeList.insert(0,QRectF(leftTop,rightBottom));
}
void Widget::rightAddRect()
{
QPointF leftTop; // 左上角坐标
QPointF rightBottom; // 右上角坐标
if (snakeList[0].x() + nodeWidth > this->width()) {
// 当蛇移动到窗口最右部:确认新坐标 x = 0
leftTop = QPointF(0, snakeList[0].y());
}
else {
// 向右移动:x坐标必然增加
leftTop = QPointF(snakeList[0].x() + nodeWidth, snakeList[0].y());
}
// 右下角坐标 = 之前一个的左上角坐标x,y + 小方块的宽高
rightBottom = leftTop + QPointF(nodeWidth, nodeHeight);
// 插入矩形小方块1个
snakeList.insert(0,QRectF(leftTop,rightBottom));
}
// 删除蛇身最后一个
void Widget::deleteLast()
{
snakeList.removeLast();
}
// 食物是随机出现的
void Widget::addFood()
{
int rectX = (qrand() % (this->width() / 20)) * 20;
int rectY = (qrand() % (this->height() / 20)) * 20;
// 控制小球出现的范围
if (rectX >= 0 && rectY>=0) {
food = QRectF(rectX, rectY, nodeWidth, nodeHeight);
qDebug()<<"food = "<<food;
}
}
// 蛇头和蛇身碰撞检查,其实就是蛇头和蛇尾其中一个方块重合
bool Widget::checkHit()
{
// 整个蛇的长度都遍历一遍
for (int i =0 ; i < snakeList.length(); i++) {
// 从蛇头后的第一个开始检查 只要有
for (int j= i+ 1; j < snakeList.length();j++) {
// rect0和rectx相等,表示它们的坐标、宽度和高度都一致
if (snakeList[0] == snakeList[j]) {
return true;
}
}
}
return false;
}
void Widget::keyPressEvent(QKeyEvent *event)
{
switch (event->key()) {
case Qt::Key::Key_Up:
if (moveFlag != dir_DOWN) {
moveFlag = dir_UP;
}
break;
case Qt::Key::Key_Down:
if (moveFlag != dir_UP) {
moveFlag = dir_DOWN ;
}
break;
case Qt::Key::Key_Left:
if (moveFlag != dir_RIGHT) {
moveFlag = dir_LEFT;
}
break;
case Qt::Key::Key_Right:
if (moveFlag != dir_LEFT) {
moveFlag = dir_RIGHT;
}
break;
case Qt::Key_Space:
if (gameStart == false) {
gameStart = true;
timer->start(startTime); // 100ms
}
else {
gameStart = false;
timer->stop();
}
break;
default:
break;
}
}
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
QPen pen;
QBrush brush;
QPixmap pix;
// 绘制图片背景
pix.load(":/icons/bg_snake.png");
painter.drawPixmap(0,0,this->width(),this->height(),pix);
// 绘制蛇
pen.setColor(Qt::color0);
brush.setColor(Qt::darkGreen); // 绿色
brush.setStyle(Qt::SolidPattern); // 实线图案
painter.setPen(pen);
painter.setBrush(brush);
for (int i = 0; i < snakeList.length(); i++) {
painter.drawRect(snakeList[i]);
}
// 分数
ui->label_score->setText(QString::number(snakeList.length() -3));
// 绘制食物
painter.drawEllipse(food);
// 蛇头碰到蛇身结束游戏
if (checkHit()) {
QFont font("方正舒体",30,QFont::ExtraLight,false);
painter.setFont(font);
painter.drawText((this->width() - 200)/2,this->height()/2,QString("游戏结束"));
timer->stop();
}
QWidget::paintEvent(event);
}
// 定时器槽函数
void Widget::my_timeout()
{
//int count = 1; // 采用这种方式也可以加长蛇身,不过我还是喜欢我的烂方法
// 判断蛇是否吃到食物 是否重合[交叉;相交;贯穿;横穿;横断] 蛇变长
if (snakeList[0].intersects(food)) {
qDebug()<<"吃到食物 snakeList[0] = "<<snakeList[0]<<" food = "<<food;
//count ++ ; // 例如 2
switch (moveFlag) {
case dir_UP:
this->topAddRect(); // +1
break;
case dir_DOWN:
this->downAddRect(); // +1
break;
case dir_LEFT:
this->leftAddRect(); // +1
break;
case dir_RIGHT:
this->rightAddRect(); // +1
break;
default:
break;
}
addFood(); // 食物位置变化
return;
}
// 加长蛇身 每次在最前面增加一个
// while (count--) {
switch (moveFlag) {
case dir_UP:
this->topAddRect(); // +1
break;
case dir_DOWN:
this->downAddRect(); // +1
break;
case dir_LEFT:
this->leftAddRect(); // +1
break;
case dir_RIGHT:
this->rightAddRect(); // +1
break;
default:
break;
}
//}
// 为了动态显示每次最前面插入一个,最后面就减少一个
deleteLast(); // 删除蛇尾 -1
this->update(); // 刷新绘制函数
}
拡大する
Qタイマー
QTimer は、Qt でのタイマー操作に使用されるクラスです。タイマーの操作を制御および管理するためによく使用される関数をいくつか提供します。以下は、一般的に使用されるいくつかの QTimer 関数の紹介です。
start(int msec)
: タイマーを開始し、指定されたミリ秒間隔でタイマーの timeout() シグナルをトリガーします。stop()
: タイマーを停止し、timeout() シグナルをトリガーしなくなります。setInterval(int msec)
: タイマー間隔をミリ秒単位で設定します。interval()
: 現在のタイマー間隔を取得します。isActive()
: タイマーがアクティブであるかどうか、つまり実行されているかどうかを判断します。setSingleShot(bool singleShot)
: タイマーの実行モードを設定します。true に設定すると、タイマーは 1 回だけトリガーされます。false (デフォルト値) に設定すると、タイマーは常にトリガーされます。singleShot(int msec, const QObject* receiver, const char* member)
: シングルトリガータイマーを作成し、トリガー時間、信号を受信するオブジェクト、および対応するスロット関数を指定します。remainingTime()
: タイマーの残りのトリガー時間をミリ秒単位で取得します。
これらの関数は基本的なタイマー操作関数を提供し、タイマー シグナル timeout() および接続 (Qt のシグナルおよびスロット メカニズム) とともに使用して、必要なタイミング操作を実現できます。
QKeyEvent
QKeyEvent は、キーボード イベントの処理に使用される Qt のクラスで、キーボード イベントに関連する情報を取得および処理するためによく使用される関数をいくつか提供します。以下は、一般的に使用されるいくつかの QKeyEvent 関数の紹介です。
key()
: キーボード イベントをトリガーするキーの Qt キーボード コードを取得し、Qt::Key 列挙値を返します。text()
: キーボード イベントをトリガーしたキーに対応するテキストを取得し、QString を返します。modifiers()
: キーボード イベントがトリガーされたときに修飾キーのステータスを取得し、Shift、Ctrl、Alt などの修飾キーのステータスを確認するために使用できる Qt::KeyboardModifiers 列挙値を返します。isAutoRepeat()
: キーボード イベントをトリガーするキーが自動的に繰り返される押下イベントであるかどうかを決定します。count()
: 自動リピートキーが連続してトリガーされた回数を取得します。nativeVirtualKey()
: 基盤となるプラットフォームの仮想キー コードを取得し、int 値を返します。nativeModifiers()
: 基盤となるプラットフォームの修飾キーのステータスを取得し、int 値を返します。
これらの関数は、どのキーが押されたか、修飾キーが同時に押されたかどうか、イベントが自動的に繰り返された回数など、キーボード イベントに関連する情報を取得するのに役立ちます。これらの関数を使用してキーボード イベントを処理し、必要に応じて対応するアクションを実行できます。
QRectF
QRectF は、浮動小数点精度で長方形領域を表すために使用される Qt のクラスで、長方形領域を操作および管理するためによく使用される関数をいくつか提供します。以下は、一般的に使用されるいくつかの QRectF 関数の紹介です。
QRectF()
: デフォルトのコンストラクター。無効な長方形領域を作成します。QRectF(qreal x, qreal y, qreal width, qreal height)
: 指定された座標、幅、高さで定義された長方形の領域を作成するコンストラクター。setRect(qreal x, qreal y, qreal width, qreal height)
:長方形領域の位置とサイズを設定します。setCoords(qreal x1, qreal y1, qreal x2, qreal y2)
: 長方形領域の左上隅と右下隅の座標を設定します。x()
、y()
、width()
、height()
: 長方形領域の左上隅の x 座標と y 座標、および幅と高さを取得します。left()
、top()
、right()
、bottom()
: 長方形領域の左、上、右、下の境界の座標を取得します。setX(qreal x)
、setY(qreal y)
、setWidth(qreal width)
、setHeight(qreal height)
: 長方形領域の左上隅の x 座標と y 座標、および幅と高さを設定します。setLeft(qreal left)
、setTop(qreal top)
、setRight(qreal right)
、setBottom(qreal bottom)
: 長方形領域の左、上、右、下の境界の座標を設定します。moveTo(qreal x, qreal y)
: 長方形領域の位置を移動し、その左上隅を指定された座標に設定します。translated(qreal dx, qreal dy)
: 長方形領域を x 方向と y 方向に指定された距離だけ移動します。contains(const QPointF &point)
: 長方形領域に指定された点が含まれているかどうかを判断します。isEmpty()
: 長方形領域が空かどうか、つまり幅または高さが 0 かどうかを判断します。isNull()
: 長方形領域が空かどうか、つまり幅と高さが 0 かどうかを判断します。
QポイントF
QPointF は、浮動小数点精度で 2 次元の点を表すために使用される Qt のクラスで、点の座標を操作および管理するためによく使用される関数をいくつか提供します。以下は、一般的に使用されるいくつかの QPointF 関数の紹介です。
QPointF()
: デフォルトのコンストラクター。ゼロ値の座標を持つ点を作成します。QPointF(qreal x, qreal y)
: 指定された座標で点を作成するコンストラクター。setX(qreal x)
,setY(qreal y)
: 点の x 座標と y 座標を設定します。x()
,y()
: 点の x 座標と y 座標を取得します。isNull()
: ポイントが空かどうか、つまり座標が 0 かどうかを判断します。manhattanLength()
: 点から座標原点までのマンハッタン距離(絶対値の和)を計算します。distanceToLine(const QLineF &line)
: 点から指定された直線までの距離を計算します。distanceToPoint(const QPointF &point)
: 点から指定された点までの距離を計算します。operator==(const QPointF &p1, const QPointF &p2)
: 2 つの点が等しいかどうかを判断します。operator!=(const QPointF &p1, const QPointF &p2)
: 2 つの点が等しくないかどうかを判断します。isNull(const QPointF &point)
: 指定された点が空かどうか、つまり座標がゼロかどうかを判断します。
これらの関数は、座標の設定、座標の取得、点が空かどうかの判断、他の点や直線からの距離の計算など、点に対する基本的な操作を提供します。これらの関数を使用して、ニーズに合わせて点の座標を作成、変更、計算できます。
Qペインター
QPainter は Qt が提供する描画用のクラスで、グラフィック、イメージ、テキストを描画する機能をカプセル化しています。以下は、一般的に使用されるいくつかの QPainter 関数の紹介です。
begin(QPaintDevice *device)
: 指定された描画デバイス上で描画を開始します。デバイスは QWidget、QImage などです。end()
:描画操作を終了します。setPen(const QPen &pen)
: 線のスタイル、色、その他の属性を定義するために使用される描画ブラシを設定します。setBrush(const QBrush &brush)
: 描画ブラシを設定して、閉じた形状の色、グラデーション、その他の属性を塗りつぶします。setRenderHint(RenderHint hint, bool on = true)
: アンチエイリアス、テキスト アンチエイリアスなどの描画レンダリングのヒントをオンまたはオフにします。drawLine(const QLine &line)
:直線を描きます。drawRect(const QRect &rect)
:長方形を描きます。drawEllipse(const QRect &rect)
:楕円を描きます。drawText(const QPointF &pos, const QString &text)
: 指定された位置にテキストを描画します。drawPixmap(const QRectF &targetRect, const QPixmap &pixmap)
: 指定された長方形の領域にピクセル マップを描画します。save()
、restore()
: 描画状態の保存と復元。描画状態の切り替えやオーバーレイ効果などを実現するために使用されます。resetTransform()
: 座標変換行列をリセットします。translate(qreal dx, qreal dy)
:翻訳ブラシの原点。scale(qreal sx, qreal sy)
: ブラシを拡大縮小して描画を比例的に調整します。rotate(qreal angle)
:原点を中心にブラシを回転させます。
これらの関数は、基本的なグラフィック、テキスト、イメージを描画する機能を提供し、ブラシ、ブラシ、レンダリング ヒントなどの属性を設定することで、さまざまなスタイルの描画効果を実現できます。同時に、移動、拡大縮小、回転などの変形機能を使用して、ブラシの描画位置や方向を変更することもできます。これらの機能を使用すると、さまざまな描画ニーズを実現し、リッチで多様なユーザー インターフェイスを作成できます。
Qアイコン
QIcon は Qt が提供するアイコンを管理するクラスで、アイコンの読み込み、表示、操作を行うことができます。以下は、一般的に使用されるいくつかの QIcon 関数の紹介です。
QIcon()
: デフォルトのコンストラクター。空のアイコンを作成します。QIcon(const QString &filename)
: コンストラクター。指定されたファイル名に従ってアイコンをロードします。QIcon(const QPixmap &pixmap)
: コンストラクター。指定されたピックスマップに基づいてアイコンを作成します。QIcon(const QIcon &other)
: コピー コンストラクター。指定されたアイコンの同一のコピーを作成します。addFile(const QString &filename, const QSize &size = QSize(), QIcon::Mode mode = QIcon::Normal, QIcon::State state = QIcon::Off)
: アイコンに画像ファイルを追加し、画像のサイズ、モード、ステータスを指定できます。isNull()
: アイコンが空かどうか、つまり画像がロードされていないかを判断します。pixmap(const QSize &size, QIcon::Mode mode = QIcon::Normal, QIcon::State state = QIcon::Off) const
: アイコンのピクセル画像を取得し、画像のサイズ、モード、ステータスを指定できます。paint(QPainter *painter, const QRect &rect, Qt::Alignment alignment = Qt::AlignCenter, QIcon::Mode mode = QIcon::Normal, QIcon::State state = QIcon::Off) const
: 指定された四角形領域内にアイコンを描画します。配置、モード、状態を指定できます。operator=(const QIcon &other)
: 代入演算子。指定されたアイコンの内容を現在のアイコンにコピーします。operator==
,operator!=
: 2 つのアイコンが等しいか等しくないかを比較するために使用されます。
必要に応じて画像ファイルを読み込み、アイコンのサイズ、モード、状態を設定し、指定した領域にアイコンを描画し、アイコンの比較や割り当てなどのアイコンの読み込み、表示、操作を行う機能を提供します。QIcon クラスは Qt のインターフェイス開発で広く使用されており、アイコンの管理と使用をシンプルかつ柔軟にします。