1 项目简介
本项目灵感来自经典的俄罗斯方块游戏(Tetris),该游戏由Alexey Pajitnov于1984年开辟。俄罗斯方块以其简单而富有挑衅性的游戏机制广受欢迎,成为了许多平台上的经典游戏。随着现代开辟工具的进步,利用Qt框架重新实现这一经典游戏不仅是对经典的致敬,也是对个人编程技能的一次提升。
《俄罗斯方块》的根本规则是移动、旋转和摆放游戏主动输出的各种方块,使之排列成完备的一行或多行并且消除得分。
a.按方向键的左右键可实现方块的左右移动;
b. 按方向键的下键可实现方块的加速下落;
c.按方向键的上键可实现方块的变形。
2 效果展示
3 代码实现
3.1 框架
3.2 UI界面
3.3 核心代码
3.3.1 TetrisGameWindow.h
- #ifndef TETRISGAMEWINDOW_H
- #define TETRISGAMEWINDOW_H
- #include <QWidget>
- #include <QPoint>
- #include "core/Subject.h"
- namespace Ui {
- class TetrisGameWindow;
- }
- class QTimer;
- namespace restonce {
- class TetrisGame;
- }
- class TetrisGameWindow
- : public QWidget, public restonce::Observer
- {
- Q_OBJECT
- public:
- explicit TetrisGameWindow(QWidget *parent = 0);
- ~TetrisGameWindow();
- protected:
- void paintEvent(QPaintEvent *) override final;
- void keyPressEvent(QKeyEvent *) override final;
- virtual void onSubjectChanged() override final;
- private slots:
- void on_pushButton_clicked();
- void slot_timeout();
- private:
- Ui::TetrisGameWindow *ui;
- restonce::TetrisGame *m_game;
- int m_boxSize = 24;
- QPoint m_basePosition = QPoint(10, 10);
- QPoint m_baseNextPosition = QPoint(200, 240);
- QTimer *m_timer;
- };
- #endif // TETRISGAMEWINDOW_H
复制代码 3.3.2 TetrisGameWindow.cpp
- #include "TetrisGameWindow.h"
- #include "ui_TetrisGameWindow.h"
- #include "core/TetrisGame.h"
- #include "core/RandomBox.h"
- #include <QPainter>
- #include <QTimer>
- #include <QKeyEvent>
- TetrisGameWindow::TetrisGameWindow(QWidget *parent) :
- QWidget(parent),
- ui(new Ui::TetrisGameWindow)
- {
- ui->setupUi(this);
- m_game = new restonce::TetrisGame;
- m_timer = new QTimer(this);
- connect(m_timer, SIGNAL(timeout()),
- this, SLOT(slot_timeout()));
- this->setFixedSize(this->size());
- m_game->attachObserver(this);
- }
- void TetrisGameWindow::slot_timeout()
- {
- m_game->timeout();
- }
- TetrisGameWindow::~TetrisGameWindow()
- {
- delete ui;
- delete m_game;
- }
- void TetrisGameWindow::on_pushButton_clicked()
- {
- m_timer->start(1000);
- m_game->start();
- }
- void TetrisGameWindow::paintEvent(QPaintEvent *)
- {
- switch(m_game->getGameStatus())
- {
- case restonce::TetrisGame::GameStatus::runing:
- ui->label->setText("正在游戏");
- ui->pushButton->setEnabled(false);
- break;
- case restonce::TetrisGame::GameStatus::stop:
- ui->label->setText("游戏结束");
- ui->pushButton->setEnabled(true);
- break;
- case restonce::TetrisGame::GameStatus::undo:
- ui->label->clear();
- ui->pushButton->setEnabled(true);
- break;
- }
- QPainter painter(this);
- QPoint p2(m_basePosition.x()+ m_boxSize*restonce::TetrisGame::ROW+1,
- m_basePosition.y() -1);
- QPoint p3(m_basePosition.x()-1,
- m_basePosition.y() +m_boxSize*restonce::TetrisGame::LINE+1);
- QPoint p4(m_basePosition.x() + m_boxSize*restonce::TetrisGame::ROW+1,
- m_basePosition.y() + m_boxSize*restonce::TetrisGame::LINE+1);
- QPoint p1(m_basePosition.x()-1, m_basePosition.y()-1);
- painter.drawLine(p1, p2);
- painter.drawLine(p2, p4);
- painter.drawLine(p4, p3);
- painter.drawLine(p1, p3);
- for(int l=0; l<restonce::TetrisGame::LINE; ++l) {
- for(int r=0; r<restonce::TetrisGame::ROW; ++r) {
- QPoint p(m_basePosition.x() + r*m_boxSize,
- m_basePosition.y() + l*m_boxSize);
- int color = 0;
- if(m_game->exists(l, r))
- {
- color = m_game->color(l, r);
- }
- else if(m_game->getActiveBox() && m_game->getActiveBox()->inBody(l, r))
- {
- color = m_game->getActiveBox()->color();
- }
- if(color <= 0)
- continue;
- QString imgpath = QString::asprintf(":/boxes/images/box%d.jpg", color);
- painter.drawImage(p, QImage(imgpath));
- }
- }
- std::shared_ptr<restonce::RandomBox> nextBox = m_game->getNextBox();
- if(nextBox) {
- QString imgpath = QString::asprintf(":/boxes/images/box%d.jpg", nextBox->color());
- for(restonce::Point const& p : nextBox->getMyBoxes()) {
- painter.drawImage(QPoint(m_baseNextPosition.x() +m_boxSize*p.row(),
- m_baseNextPosition.y() + m_boxSize*p.line()),
- QImage(imgpath));
- }
- }
- }
- void TetrisGameWindow::keyPressEvent(QKeyEvent *e)
- {
- switch(e->key())
- {
- case Qt::Key_Down:
- m_game->down();
- break;
- case Qt::Key_Left:
- m_game->left();
- break;
- case Qt::Key_Right:
- m_game->right();
- break;
- case Qt::Key_Up:
- m_game->transform();
- break;
- }
- }
- void TetrisGameWindow::onSubjectChanged()
- {
- repaint();
- }
复制代码 3.3.3 Subject.h
- #ifndef RESTONCE_SUBJECT_H
- #define RESTONCE_SUBJECT_H
- #include <set>
- namespace restonce {
- class Observer
- {
- protected:
- Observer() = default;
- virtual ~Observer() = default;
- public:
- virtual void onSubjectChanged() = 0;
- };
- class Subject
- {
- public :
- void attachObserver(Observer *o);
- protected:
- Subject() = default;
- virtual ~Subject() = default;
- void notifyObservers() ;
- private:
- std::set<Observer *> m_observers;
- };
- } // namespace restonce
- #endif // RESTONCE_SUBJECT_H
复制代码 3.3.4 Subject.cpp
- #include "core/Subject.h"
- namespace restonce {
- void Subject::attachObserver(Observer *o)
- {
- m_observers.insert(o);
- }
- void Subject::notifyObservers()
- {
- for(Observer *o : m_observers)
- {
- o->onSubjectChanged();
- }
- }
- } // namespace restonce
复制代码 3.3.5 TetrisGame.h
- #ifndef RESTONCE_TETRISGAME_H
- #define RESTONCE_TETRISGAME_H
- #include <memory>
- #include <random>
- #include "Subject.h"
- namespace restonce {
- class RandomBox;
- class TetrisGame
- : public Subject
- {
- public:
- enum class GameStatus {
- undo, runing, stop
- };
- enum class WinStatus {
- win, lose
- };
- enum {
- ROW = 10,
- LINE = 18
- };
- TetrisGame();
- // 用户通过gui可以对游戏进行的操作
- void start();
- void timeout();
- void transform();
- void down();
- void left();
- void right();
- void stop();
- // gui更新时会用以下函数读取游戏状态
- GameStatus getGameStatus() const;
- WinStatus getWinStatus() const;
- // 是否存在方块,如果越界会抛出异常
- bool exists(int line, int row) const;
- int color(int line, int row) const;
- std::shared_ptr<RandomBox> getActiveBox() const;
- // 下一个Box
- std::shared_ptr<RandomBox> getNextBox() const;
- // 某位置是否越界
- bool valid(int line, int row) const;
- // 填充某个位置
- void set(int line, int row, int color);
- private:
- void init();
- private:
- GameStatus m_gameStatus;
- WinStatus m_winStatus;
- bool m_map[LINE][ROW] ;
- int m_colorMap[LINE][ROW] ;
- std::shared_ptr<RandomBox> m_activebox, m_nextBox;
- std::mt19937 m_rd;
- };
- } // namespace restonce
- #endif // RESTONCE_TETRISGAME_H
复制代码 3.3.6 TetrisGame.cpp
- #include "TetrisGame.h"
- #include "RandomBox.h"
- #include <time.h>
- namespace restonce {
- TetrisGame::TetrisGame()
- {
- m_gameStatus = GameStatus::undo;
- m_winStatus = WinStatus::lose;
- m_rd.seed(time(NULL));
- for(int l=0; l<LINE; ++l) {
- for(int r=0; r<ROW; ++r) {
- m_map[l][r] = false;
- }
- }
- }
- void TetrisGame::init()
- {
- for(int l=0; l<LINE; ++l) {
- for(int r=0; r<ROW; ++r) {
- m_map[l][r] = false;
- m_colorMap[l][r] = 0;
- }
- }
- m_gameStatus = GameStatus::undo;
- m_winStatus = WinStatus::lose;
- m_activebox = std::make_shared<RandomBox>(*this, m_rd);
- m_nextBox = std::make_shared<RandomBox>(*this, m_rd);
- }
- void TetrisGame::start()
- {
- if(m_gameStatus == GameStatus::runing) {
- throw std::logic_error("Game is runing !");
- }
- init();
- m_gameStatus = GameStatus::runing;
- notifyObservers();
- }
- void TetrisGame::timeout()
- {
- if(m_gameStatus != GameStatus::runing) {
- return ;
- }
- if(!m_activebox->down()) {
- // 此处准备消行
- for(int line=LINE-1; line>=0; --line) {
- bool isFull = true;
- for(int row=0; row<ROW; ++row) {
- if(!this->exists(line, row)) {
- isFull = false;
- break;
- }
- }
- if(isFull) {
- for(int l=line; l>=0; --l) {
- for(int r=0; r<ROW; ++r) {
- if(l ==0) {
- m_map[l][r] = false;
- } else {
- m_map[l][r] = m_map[l-1][r];
- }
- }
- }
- ++ line;
- }
- }
- //
- m_activebox =m_nextBox;
- m_nextBox=std::make_shared<RandomBox>(*this, m_rd);
- if(!m_activebox->valid()) {
- // 新产生的方块不合法,说明你已经输了
- m_gameStatus = GameStatus::stop;
- m_winStatus = WinStatus::lose;
- }
- }
- notifyObservers();
- }
- void TetrisGame::transform()
- {
- if(m_activebox->transform()) {
- notifyObservers();
- }
- }
- void TetrisGame::down()
- {
- timeout();
- }
- void TetrisGame::left()
- {
- if(m_activebox->left()) {
- notifyObservers();
- }
- }
- void TetrisGame::right()
- {
- if(m_activebox->right()) {
- notifyObservers();
- }
- }
- void TetrisGame::stop()
- {
- if(m_gameStatus == GameStatus::runing) {
- m_gameStatus = GameStatus::stop;
- m_winStatus = WinStatus::lose;
- notifyObservers();
- }
- }
- TetrisGame::GameStatus TetrisGame::getGameStatus() const
- {
- return m_gameStatus;
- }
- TetrisGame::WinStatus TetrisGame::getWinStatus() const
- {
- if(m_gameStatus != GameStatus::stop) {
- throw std::logic_error("Game is not stop !");
- }
- return m_winStatus;
- }
- bool TetrisGame::valid(int line, int row) const
- {
- return line >=0 && line < LINE &&
- row >=0 && row < ROW;
- }
- bool TetrisGame::exists(int line, int row) const
- {
- if(!valid(line, row)) {
- throw std::out_of_range("Game position not exists !");
- }
- return m_map[line][row];
- }
- int TetrisGame::color(int line, int row) const
- {
- return m_colorMap[line][row];
- }
- std::shared_ptr<RandomBox> TetrisGame::getActiveBox() const
- {
- return m_activebox;
- }
- void TetrisGame::set(int line, int row, int color)
- {
- m_map[line][row] = true;
- m_colorMap[line][row] = color;
- }
- std::shared_ptr<RandomBox> TetrisGame::getNextBox() const
- {
- return m_nextBox;
- }
- } // namespace restonce
复制代码 4 运行效果
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |