21xrx.com
2024-12-27 04:46:27 Friday
登录
文章检索 我的文章 写文章
使用Qt开发舒尔格游戏的C++教程
2023-07-01 12:21:23 深夜i     --     --
Qt 舒尔格游戏 C++ 教程 开发

舒尔格游戏(Schulte Grid)是一个测试反应力和注意力的小游戏,其原理是在一个方阵中随机排列数字,并要求玩家按照从小到大的顺序依次点击。在开发这个小游戏时,我们可以选择使用Qt作为开发工具,下面是一份使用Qt开发舒尔格游戏的C++教程,供大家参考。

1. 创建一个Qt项目

首先,我们需要在Qt中创建一个项目。选择File > New Project,然后在弹出的对话框中选择Qt Widgets Application。

2. 创建一个QWidget

在Qt中,我们可以将舞台理解为QWidget,因此我们需要创建一个QWidget,并设置其大小和背景。我们可以在头文件(.h)中定义一个新的类TowerDefenseWidget,并在实现文件(.cpp)中定义其具体的实现:


// TowerDefenseWidget.h

#ifndef TOWERDEFENSEWIDGET_H

#define TOWERDEFENSEWIDGET_H

#include <QWidget>

class TowerDefenseWidget : public QWidget

{

  Q_OBJECT

public:

  explicit TowerDefenseWidget(QWidget *parent = nullptr);

protected:

  void paintEvent(QPaintEvent *event) override;

private:

  void drawGrid(QPainter &painter, int size);

  void drawText(QPainter &painter, int fontSize, const QString &text,

         const QPointF &point, Qt::AlignmentFlag flags);

  int m_gridSize;

};

#endif // TOWERDEFENSEWIDGET_H

// TowerDefenseWidget.cpp

#include "TowerDefenseWidget.h"

#include <QPainter>

#include <QFont>

TowerDefenseWidget::TowerDefenseWidget(QWidget *parent)

  : QWidget(parent), m_gridSize(30)

{

  setFixedSize(m_gridSize * 5, m_gridSize * 5);

  setStyleSheet("background-color: white;");

  setWindowTitle(tr("Schulte Grid"));

}

void TowerDefenseWidget::paintEvent(QPaintEvent *event)

{

  Q_UNUSED(event);

  QPainter painter(this);

  painter.setRenderHint(QPainter::Antialiasing);

  drawGrid(painter, m_gridSize);

  drawText(painter, 20, "Schulte Grid", QPointF(width() / 2.0, 25), Qt::AlignHCenter);

}

void TowerDefenseWidget::drawGrid(QPainter &painter, int size)

{

  painter.save();

  painter.setPen(QPen(Qt::black, 1));

  for (int x = 0; x < 5; x++) {

    for (int y = 0; y < 5; y++) {

      QRectF rect(x * size, y * size, size, size);

      painter.drawRect(rect);

    }

  }

  painter.restore();

}

void TowerDefenseWidget::drawText(QPainter &painter, int fontSize, const QString &text,

                 const QPointF &point, Qt::AlignmentFlag flags)

{

  painter.save();

  QFont font = painter.font();

  font.setPixelSize(fontSize);

  painter.setFont(font);

  QRectF textRect = painter.boundingRect(QRectF(), flags, text);

  QPointF textPos = point - QPointF(textRect.width() / 2.0, textRect.height() / 2.0);

  painter.drawText(textPos, text);

  painter.restore();

}

在这里,我们定义了一个QWidget,并设置了其大小为150x150,背景颜色为白色,并在顶部居中显示了文字“Schulte Grid”。

3. 渲染数字方格

接下来,我们需要在QWidget上渲染数字方格。每个方格都由一个数字表示,玩家需要按照数字从小到大的顺序依次点击方格。我们可以在头文件(.h)中添加一些类变量和方法,方便我们在实现文件(.cpp)中渲染方格:


// TowerDefenseWidget.h

#ifndef TOWERDEFENSEWIDGET_H

#define TOWERDEFENSEWIDGET_H

#include <QWidget>

#include <vector>

class TowerDefenseWidget : public QWidget

{

  Q_OBJECT

public:

  explicit TowerDefenseWidget(QWidget *parent = nullptr);

protected:

  void paintEvent(QPaintEvent *event) override;

  void mousePressEvent(QMouseEvent *event) override;

private:

  void drawGrid(QPainter &painter, int size);

  void drawText(QPainter &painter, int fontSize, const QString &text,

         const QPointF &point, Qt::AlignmentFlag flags);

  int m_gridSize;

  std::vector<int> m_gridValues;

  std::vector<int> m_gridOrder;

};

#endif // TOWERDEFENSEWIDGET_H

在实现文件(.cpp)中,我们可以添加以下代码:


// TowerDefenseWidget.cpp

#include "TowerDefenseWidget.h"

#include <QPainter>

#include <QFont>

#include <algorithm>

#include <random>

TowerDefenseWidget::TowerDefenseWidget(QWidget *parent)

  : QWidget(parent), m_gridSize(30)

{

  setFixedSize(m_gridSize * 5, m_gridSize * 5);

  setStyleSheet("background-color: white;");

  setWindowTitle(tr("Schulte Grid"));

  // 随机生成25个数字

  for (int i = 1; i <= 25; ++i) {

    m_gridValues.push_back(i);

  }

  std::random_device rd;

  std::mt19937 g(rd());

  std::shuffle(m_gridValues.begin(), m_gridValues.end(), g);

  // 计算25个数字的顺序,并打乱

  for (int i = 0; i < 25; ++i) {

    m_gridOrder.push_back(i);

  }

  std::shuffle(m_gridOrder.begin(), m_gridOrder.end(), g);

}

void TowerDefenseWidget::paintEvent(QPaintEvent *event)

{

  Q_UNUSED(event);

  QPainter painter(this);

  painter.setRenderHint(QPainter::Antialiasing);

  drawGrid(painter, m_gridSize);

  for (int i = 0; i < 25; ++i) {

    int value = m_gridValues[m_gridOrder[i]];

    QPointF pos(i % 5, i / 5);

    QRectF rect(pos * m_gridSize, QSizeF(m_gridSize, m_gridSize));

    drawText(painter, 20, QString::number(value), rect.center(), Qt::AlignCenter);

  }

}

void TowerDefenseWidget::mousePressEvent(QMouseEvent *event)

{

  if (event->button() == Qt::LeftButton) {

    int index = (event->pos().y() / m_gridSize) * 5 + (event->pos().x() / m_gridSize);

    if (index == m_gridOrder.front()) {

      m_gridOrder.erase(m_gridOrder.begin());

      if (m_gridOrder.empty()) {

        QMessageBox::information(this, tr("Game Over"), tr("Congratulations!"));

      } else {

        update();

      }

    } else {

      QMessageBox::warning(this, tr("Wrong"), tr("Please click the next number!"));

    }

  }

}

void TowerDefenseWidget::drawGrid(QPainter &painter, int size)

{

  painter.save();

  painter.setPen(QPen(Qt::black, 1));

  for (int x = 0; x < 5; x++) {

    for (int y = 0; y < 5; y++) {

      QRectF rect(x * size, y * size, size, size);

      painter.drawRect(rect);

    }

  }

  painter.restore();

}

void TowerDefenseWidget::drawText(QPainter &painter, int fontSize, const QString &text,

                 const QPointF &point, Qt::AlignmentFlag flags)

{

  painter.save();

  QFont font = painter.font();

  font.setPixelSize(fontSize);

  painter.setFont(font);

  QRectF textRect = painter.boundingRect(QRectF(), flags, text);

  QPointF textPos = point - QPointF(textRect.width() / 2.0, textRect.height() / 2.0);

  painter.drawText(textPos, text);

  painter.restore();

}

在这里,我们添加了两个变量:m_gridValues和m_gridOrder。m_gridValues数组中保存了所有需要渲染的数字,m_gridOrder数组中保存了所有数字的顺序,并进行了打乱处理,以便让游戏更具挑战性。

在paintEvent方法中,我们使用循环遍历m_gridOrder数组,并使用其值作为索引从m_gridValues数组中获取相应的数字,然后绘制在对应的方格中。

在mousePressEvent方法中,我们首先通过计算鼠标点击位置所在的方格位置,并与m_gridOrder.front()进行比较来判断是否点击了正确的数字。如果点击了正确的数字,则将m_gridOrder.front()删除,并调用update()方法进行重新绘制。如果m_gridOrder为空,则表示游戏结束,弹出恭喜消息框。如果点击了错误的数字,则弹出错误消息框。

4. 增加游戏开始按钮

为了让游戏更加友好,我们可以添加一个按钮,让玩家可以随时开始游戏。我们可以在头文件(.h)中添加一个QPushButon对象,并在实现文件(.cpp)中创建该对象,设置其位置和大小以及样式表:


// TowerDefenseWidget.h

#ifndef TOWERDEFENSEWIDGET_H

#define TOWERDEFENSEWIDGET_H

#include <QWidget>

#include <vector>

class QPushButton;

class TowerDefenseWidget : public QWidget

{

  Q_OBJECT

public:

  explicit TowerDefenseWidget(QWidget *parent = nullptr);

protected:

  void paintEvent(QPaintEvent *event) override;

  void mousePressEvent(QMouseEvent *event) override;

private:

  void drawGrid(QPainter &painter, int size);

  void drawText(QPainter &painter, int fontSize, const QString &text,

         const QPointF &point, Qt::AlignmentFlag flags);

  int m_gridSize;

  std::vector<int> m_gridValues;

  std::vector<int> m_gridOrder;

  QPushButton *m_startButton;

};

#endif // TOWERDEFENSEWIDGET_H

// TowerDefenseWidget.cpp

#include "TowerDefenseWidget.h"

#include <QPainter>

#include <QFont>

#include <QHBoxLayout>

#include <QMessageBox>

#include <algorithm>

#include <random>

TowerDefenseWidget::TowerDefenseWidget(QWidget *parent)

  : QWidget(parent), m_gridSize(30)

{

  setFixedSize(m_gridSize * 5, m_gridSize * 5);

  setStyleSheet("background-color: white;");

  setWindowTitle(tr("Schulte Grid"));

  QHBoxLayout *layout = new QHBoxLayout(this);

  m_startButton = new QPushButton(tr("Start"), this);

  layout->addWidget(m_startButton, 0, Qt::AlignCenter);

  setLayout(layout);

  connect(m_startButton, &QPushButton::clicked, [this](){

    std::random_device rd;

    std::mt19937 g(rd());

    // 随机生成25个数字

    for (int i = 1; i <= 25; ++i) {

      m_gridValues.push_back(i);

    }

    std::shuffle(m_gridValues.begin(), m_gridValues.end(), g);

    // 计算25个数字的顺序,并打乱

    for (int i = 0; i < 25; ++i) {

      m_gridOrder.push_back(i);

    }

    std::shuffle(m_gridOrder.begin(), m_gridOrder.end(), g);

    m_startButton->hide();

    update();

  });

  m_startButton->setStyleSheet("font-size: 20px; padding: 10px;");

}

void TowerDefenseWidget::paintEvent(QPaintEvent *event)

{

  Q_UNUSED(event);

  QPainter painter(this);

  painter.setRenderHint(QPainter::Antialiasing);

  drawGrid(painter, m_gridSize);

  if (!m_gridOrder.empty()) {

    for (int i = 0; i < 25; ++i) {

      int value = m_gridValues[m_gridOrder[i]];

      QPointF pos(i % 5, i / 5);

      QRectF rect(pos * m_gridSize, QSizeF(m_gridSize, m_gridSize));

      drawText(painter, 20, QString::number(value), rect.center(), Qt::AlignCenter);

    }

  } else {

    drawText(painter, 20, "Game Over", QPointF(width() / 2.0, height() / 2.0), Qt::AlignHCenter | Qt::AlignVCenter);

  }

}

void TowerDefenseWidget::mousePressEvent(QMouseEvent *event)

{

  if (!m_gridOrder.empty() && event->button() == Qt::LeftButton) {

    int index = (event->pos().y() / m_gridSize) * 5 + (event->pos().x() / m_gridSize);

    if (index == m_gridOrder.front()) {

      m_gridOrder.erase(m_gridOrder.begin());

      if (m_gridOrder.empty()) {

        QMessageBox::information(this, tr("Game Over"), tr("Congratulations!"));

      } else {

        update();

      }

    } else {

      QMessageBox::warning(this, tr("Wrong"), tr("Please click the next number!"));

    }

  }

}

void TowerDefenseWidget::drawGrid(QPainter &painter, int size)

{

  painter.save();

  painter.setPen(QPen(Qt::black, 1));

  for (int x = 0; x < 5; x++) {

    for (int y = 0; y < 5; y++) {

      QRectF rect(x * size, y * size, size, size);

      painter.drawRect(rect);

    }

  }

  painter.restore();

}

void TowerDefenseWidget::drawText(QPainter &painter, int fontSize, const QString &text,

                 const QPointF &point, Qt::AlignmentFlag flags)

{

  painter.save();

  QFont font = painter.font();

  font.setPixelSize(fontSize);

  painter.setFont(font);

  QRectF textRect = painter.boundingRect(QRectF(), flags, text);

  QPointF textPos = point - QPointF(textRect.width() / 2.0, textRect.height() / 2.0);

  painter.drawText(textPos, text);

  painter.restore();

}

在这里,我们首先在头文件(.h)中添加了一个QPushButton指针,在实现文件(.cpp)中创建了该按钮,并设置了其样式表。在按钮被点击时,我们通过std::random_device和std::mt19937实现了随机生成数字,并打乱数字的顺序的功能。点击开始按钮后,我们将其隐藏,并调用update()方法进行重新绘制。

5. 总结

使用Qt开发舒尔格游戏是一项有趣而且又具挑战的任务。通过这个教程,我们学会了如何使用Qt绘制数字方格、随机生成数字并打乱顺序、处理鼠标事件和添加按钮等功能。学完本教程后,你可以进一步挑战自己,尝试添加计时器、音效和排行榜等功能,让游戏更加完善。

  
  

评论区

{{item['qq_nickname']}}
()
回复
回复