diff --git a/TetrisTask.pro b/TetrisTask.pro index f4de819..279a108 100644 --- a/TetrisTask.pro +++ b/TetrisTask.pro @@ -10,11 +10,19 @@ CONFIG += c++17 SOURCES += \ gamefield.cpp \ + gameoverdialog.cpp \ main.cpp \ + modaldialog.cpp \ + nextfiguregamegrid.cpp \ + tetramino.cpp \ tetris.cpp HEADERS += \ gamefield.h \ + gameoverdialog.h \ + modaldialog.h \ + nextfiguregamegrid.h \ + tetramino.h \ tetris.h FORMS += \ diff --git a/gamefield.cpp b/gamefield.cpp index 6a5ce13..335a16b 100644 --- a/gamefield.cpp +++ b/gamefield.cpp @@ -1,32 +1,256 @@ #include "gamefield.h" #include +#include +#include +#include +#include -GameField::GameField(QWidget *parent) : QWidget{parent} { - connect(this, &GameField::InitialisationStarted, this, &GameField::SetCells, - Qt::QueuedConnection); +GameField::GameField(QWidget *parent) : QWidget(parent) { + setStyleSheet("border: 4px solid black;"); - emit InitialisationStarted(); + SetColumnNumber(20); + SetRowsNumber(24); + + gameGrid_ = QVector>(rowsNumber_, QVector(columnsNumber_, 0)); + + currentFigureRow_ = 0; + currentFigureColumn_ = 0; +} + +void GameField::SetNextFigureGrid(QVector> figure) { + nextFigure_ = figure; } uint GameField::GetRowsNumber() const { return rowsNumber_; } void GameField::SetRowsNumber(uint rowsNumber) { - if (rowsNumber_ == rowsNumber) return; + if (rowsNumber_ == rowsNumber) return; - rowsNumber_ = rowsNumber; - emit RowsNumberChanged(); + rowsNumber_ = rowsNumber; + emit RowsNumberChanged(); + update(); } uint GameField::GetColumnsNumber() const { return columnsNumber_; } void GameField::SetColumnNumber(uint columnsCount) { - if (columnsNumber_ == columnsCount) return; + if (columnsNumber_ == columnsCount) return; + + columnsNumber_ = columnsCount; + emit ColumnsNumberChanged(); + update(); +} + +void GameField::SetFigurePosition(int row, int column) { + if (column >= 0 && column + currentFigure_[0].size() <= columnsNumber_ && gameGrid_[row][column] != 1) { + currentFigureRow_ = row; + currentFigureColumn_ = column; + } + update(); +} + +void GameField::SpawnNextFigure() { + currentFigure_ = nextFigure_; + figureColor_ = Qt::blue; + emit FigureSpawned(); +} + +QVector> GameField::GetCurrentFigure() { + return currentFigure_; +} + +QVector> GameField::GetRotateCurrentFigure() { + QVector> currFigure = GetCurrentFigure(); + QVector> RotateFigure; + + RotateFigure.resize(currFigure[0].size()); + + for (int i = 0; i < RotateFigure.size(); ++i) { + RotateFigure[i].resize(currFigure.size()); + } + + + for(int i = 0; i < currFigure.size(); i++) { + for (int j = 0; j < currFigure[0].size(); j++) { + RotateFigure[j][currFigure.size() - i - 1] = currFigure[i][j]; + } + } + + currentFigure_ = RotateFigure; + + emit CurrentFigureChanged(); + return currentFigure_; +} + +void GameField::UpdateGameGrid() { + for (int i = 0; i < currentFigure_.size(); ++i) { + for (int j = 0; j < currentFigure_[0].size(); ++j) { + if (currentFigure_[i][j] == 1) { + int x = currentFigureRow_ + i; + int y = currentFigureColumn_ + j; + CheckingLine(); + gameGrid_[x][y] = 1; + } + } + } +} + +bool GameField::HasCollisionMove(int x0, int y0) { + for (int i = 0; i < currentFigure_.size(); i++) { + for (int j = 0; j < currentFigure_[0].size(); j++) { + if (currentFigure_[i][j] == 1) { + int x = currentFigureRow_ + i + x0; + int y = currentFigureColumn_ + j + y0; + + if (x < 0 || x >= rowsNumber_ || y < 0 || y >= columnsNumber_ || gameGrid_[x][y] == 1) { + return true; + } + } + } + } + return false; +} + +bool GameField::HasCollisionRotation() { + for (int i = 0; i < currentFigure_.size(); i++) { + for (int j = 0; j < currentFigure_[0].size(); j++) { + if (currentFigure_[i][j] == 1) { + int x = currentFigureRow_ + i; + int y = currentFigureColumn_ + j; + + if (x >= rowsNumber_ || y < 0 || y >= columnsNumber_ || gameGrid_[x][y] == 1) { + return true; + } + } + } + } + return false; +} + +bool GameField::HasFullLine(int row) { + for(int j = 0; j < columnsNumber_; j++) { + if(gameGrid_[row][j] != 1) { + return false; + } + } + return true; +} + +void GameField::RemoveFullLine(int row) { + for (int j = 0; j < columnsNumber_; j++) { + gameGrid_[row][j] = 0; + } + + for(int k = row;k > 0;k--) { + for(int j = 0;j < columnsNumber_; j++) { + gameGrid_[k][j] = gameGrid_[k-1][j]; + } + } +} + +int GameField::GetWinPoints() { + return winPoints_; +} + +void GameField::CheckingLine() { + for(int i = rowsNumber_ - 1; i >= 0; i--) { + if (HasFullLine(i)) { + RemoveFullLine(i); + winPoints_ += 100; + emit ChangeWinPoints(); + } + } +} + +void GameField::MoveFigure() { + GetGameOver(); + int CenterPointX = 2; + + if(!isGameOver_) { + if (currentFigureRow_ + currentFigure_.size() < rowsNumber_) { + bool hasCollision = HasCollisionMove(1, 0); + + if (hasCollision) { + UpdateGameGrid(); + SpawnNextFigure(); + SetFigurePosition(0, columnsNumber_ / CenterPointX); + CheckingLine(); + } else { + SetFigurePosition(currentFigureRow_ + 1, currentFigureColumn_); + } + } else { + UpdateGameGrid(); + SpawnNextFigure(); + SetFigurePosition(0, columnsNumber_ / CenterPointX); + CheckingLine(); + } + update(); + } else { + isGameOver_ = false; + emit GameOver(); + } +} - columnsNumber_ = columnsCount; - emit ColumnsNumberChanged(); +void GameField::GetGameOver() { + for (int i = 0; i < columnsNumber_; i++) { + if (gameGrid_[0][i] != 0) { + isGameOver_ = true; + } + } } -void GameField::SetCells() { - qDebug() << rowsNumber_ << " " << columnsNumber_ << "|"; +uint GameField::GetCurrentFigureRow() { + return currentFigureRow_; +} + +uint GameField::GetCurrentFigureColumn() { + return currentFigureColumn_; +} + +void GameField::ClearGameGrid() { + gameGrid_ = QVector>(rowsNumber_, QVector(columnsNumber_, 0)); + + currentFigure_ = QVector>(); + currentFigureRow_ = 0; + currentFigureColumn_ = 0; + + update(); +} + +void GameField::SetWinPoints(int points) { + winPoints_ = points; + emit ChangeWinPoints(); +} + +void GameField::paintEvent(QPaintEvent *event) { + Q_UNUSED(event); + QPainter painter(this); + + painter.setPen(Qt::black); + painter.fillRect(rect(), Qt::white); + + for (int i = 0; i < currentFigure_.size(); ++i) { + for (int j = 0; j < currentFigure_[0].size(); ++j) { + if (currentFigure_[i][j] == 1) { + int x = (currentFigureColumn_ + j) * blockSize_; + int y = (currentFigureRow_ + i) * blockSize_; + painter.fillRect(x, y, blockSize_, blockSize_, figureColor_); + } + } + } + + for (int i = 0; i < rowsNumber_; i++) { + for (int j = 0; j < columnsNumber_; j++) { + if (gameGrid_[i][j] == 1) { + int x = j * blockSize_; + int y = i * blockSize_; + painter.fillRect(x, y, blockSize_, blockSize_, Qt::blue); + painter.drawRect(j * blockSize_, i * blockSize_, blockSize_, blockSize_); + } else { + painter.drawRect(j * blockSize_, i * blockSize_, blockSize_, blockSize_); + } + } + } + update(); } diff --git a/gamefield.h b/gamefield.h index e36baa8..727c678 100644 --- a/gamefield.h +++ b/gamefield.h @@ -1,36 +1,96 @@ #ifndef GAMEFIELD_H #define GAMEFIELD_H +#include "tetramino.h" +#include "nextfiguregamegrid.h" + #include +class Tetris; + class GameField : public QWidget { Q_OBJECT public: explicit GameField(QWidget *parent = nullptr); - Q_PROPERTY(uint rowsNumber READ GetRowsNumber WRITE SetRowsNumber NOTIFY + Q_PROPERTY(uint rowsNumber READ GetRowsNumber WRITE SetRowsNumber NOTIFY RowsNumberChanged FINAL) - Q_PROPERTY(uint columnsNumber READ GetColumnsNumber WRITE SetColumnNumber + Q_PROPERTY(uint columnsNumber READ GetColumnsNumber WRITE SetColumnNumber NOTIFY ColumnsNumberChanged FINAL) signals: - void RowsNumberChanged(); - void ColumnsNumberChanged(); - void InitialisationStarted(); - - private slots: - void SetCells(); + void RowsNumberChanged(); + void ColumnsNumberChanged(); + void FigureSpawned(); + void FigureFalling(); + void CurrentFigureChanged(); + void ChangeWinPoints(); + void GameOver(); public: - uint GetRowsNumber() const; - void SetRowsNumber(uint newRowsNumber); + void SetNextFigureGrid(QVector> figure); + + uint GetRowsNumber() const; + void SetRowsNumber(uint newRowsNumber); + + uint GetColumnsNumber() const; + void SetColumnNumber(uint newColumnsCount); + + void SetFigurePosition(int row, int column); + + void SpawnNextFigure(); + void UpdateGameGrid(); + + bool HasCollisionMove(int xO, int yO); + + bool HasCollisionRotation(); + + QVector> GetCurrentFigure(); + QVector> GetRotateCurrentFigure(); + + bool HasFullLine(int row); + void CheckingLine(); + + void RemoveFullLine(int row); - uint GetColumnsNumber() const; - void SetColumnNumber(uint newColumnsCount); + int GetWinPoints(); + + void MoveFigure(); + + void GetGameOver(); + + uint GetCurrentFigureRow(); + uint GetCurrentFigureColumn(); + + void ClearGameGrid(); + void SetWinPoints(int points); + +protected: + void paintEvent(QPaintEvent *event); private: - uint rowsNumber_ = 0; - uint columnsNumber_ = 0; + uint rowsNumber_ = 0; + uint columnsNumber_ = 0; + int currentRow_ = 0; + + int blockSize_ = 30; + int winPoints_ = 0; + + bool isLineFull_ = true; + bool isGameOver_ = false; + + QVector> currentFigure_; + QVector> nextFigure_; + + QVector> gameGrid_; + + uint currentFigureRow_; + uint currentFigureColumn_; + + QColor figureColor_; + + Tetramino *tetraminoGeneratorFirst_; + NextFigureGameGrid *nextGameGrid_; }; #endif // GAMEFIELD_H diff --git a/gameoverdialog.cpp b/gameoverdialog.cpp new file mode 100644 index 0000000..3dc6c5e --- /dev/null +++ b/gameoverdialog.cpp @@ -0,0 +1,14 @@ +#include "gameoverdialog.h" + +GameOverDialog::GameOverDialog(QWidget *parent) : QDialog{parent} { + QVBoxLayout *layout = new QVBoxLayout(this); + + label_ = new QLabel("Вы проиграли!", this); + layout->addWidget(label_); + + newTryButton_ = new QPushButton("Попробуйте еще раз!", this); + connect(newTryButton_, &QPushButton::clicked, this, &QDialog::accept); + layout->addWidget(newTryButton_); + + setLayout(layout); +} diff --git a/gameoverdialog.h b/gameoverdialog.h new file mode 100644 index 0000000..8c81738 --- /dev/null +++ b/gameoverdialog.h @@ -0,0 +1,20 @@ +#ifndef GAMEOVERDIALOG_H +#define GAMEOVERDIALOG_H + +#include +#include +#include +#include +#include + +class GameOverDialog : public QDialog { + Q_OBJECT +public: + explicit GameOverDialog(QWidget *parent = nullptr); + +private: + QPushButton *newTryButton_; + QLabel *label_; +}; + +#endif // GAMEOVERDIALOG_H diff --git a/modaldialog.cpp b/modaldialog.cpp new file mode 100644 index 0000000..5ec046d --- /dev/null +++ b/modaldialog.cpp @@ -0,0 +1,14 @@ +#include "modaldialog.h" + +ModalDialog::ModalDialog(QWidget *parent) : QDialog{parent} { + QVBoxLayout *layout = new QVBoxLayout(this); + + label_ = new QLabel("ИГРА НА ПАУЗЕ", this); + layout->addWidget(label_); + + okButton_ = new QPushButton("ПРОДОЛЖИТЬ", this); + connect(okButton_, &QPushButton::clicked, this, &QDialog::accept); + layout->addWidget(okButton_); + + setLayout(layout); +} diff --git a/modaldialog.h b/modaldialog.h new file mode 100644 index 0000000..cbb5e55 --- /dev/null +++ b/modaldialog.h @@ -0,0 +1,20 @@ +#ifndef MODALDIALOG_H +#define MODALDIALOG_H + +#include +#include +#include +#include +#include + +class ModalDialog : public QDialog { + Q_OBJECT +public: + explicit ModalDialog(QWidget *parent = nullptr); + +private: + QPushButton *okButton_; + QLabel *label_; +}; + +#endif // MODALDIALOG_H diff --git a/nextfiguregamegrid.cpp b/nextfiguregamegrid.cpp new file mode 100644 index 0000000..105676f --- /dev/null +++ b/nextfiguregamegrid.cpp @@ -0,0 +1,39 @@ +#include "nextfiguregamegrid.h" + +NextFigureGameGrid::NextFigureGameGrid(QWidget *parent) : QWidget(parent), blockSize_(30) { + setFixedSize(120, 120); + + tetramino_ = new Tetramino(this); + + nextFigure_ = QVector>(); +} + +void NextFigureGameGrid::SetNextFigure() { + nextFigure_ = tetramino_->GetCurrentFigure(); + emit NextFigureChanged(); + update(); +} +QVector> NextFigureGameGrid::GetNextFigure() { + return nextFigure_; +} + +void NextFigureGameGrid::paintEvent(QPaintEvent *event) { + Q_UNUSED(event); + QPainter painter(this); + painter.setPen(Qt::black); + painter.fillRect(rect(), Qt::white); + + for (int i = 0; i < nextFigure_.size(); ++i) { + for (int j = 0; j < nextFigure_[0].size(); ++j) { + if (nextFigure_[i][j] == 1) { + int x = j * blockSize_; + int y = i * blockSize_; + painter.fillRect(x, y, blockSize_, blockSize_, Qt::blue); + painter.drawRect(x, y, blockSize_, blockSize_); + } else { + painter.drawRect(j * blockSize_, i * blockSize_, blockSize_, blockSize_); + } + } + } + update(); +} diff --git a/nextfiguregamegrid.h b/nextfiguregamegrid.h new file mode 100644 index 0000000..9539f58 --- /dev/null +++ b/nextfiguregamegrid.h @@ -0,0 +1,29 @@ +#ifndef NEXTFIGUREGAMEGRID_H +#define NEXTFIGUREGAMEGRID_H + +#include +#include +#include +#include +#include "tetramino.h" + +class NextFigureGameGrid : public QWidget { + Q_OBJECT +public: + explicit NextFigureGameGrid(QWidget *parent = nullptr); + void SetNextFigure(); + + QVector> GetNextFigure(); +signals: + void NextFigureChanged(); + +protected: + void paintEvent(QPaintEvent *event) override; + +private: + QVector> nextFigure_; + int blockSize_; + + Tetramino *tetramino_; +}; +#endif // NEXTFIGUREGAMEGRID_H diff --git a/tetramino.cpp b/tetramino.cpp new file mode 100644 index 0000000..a63014d --- /dev/null +++ b/tetramino.cpp @@ -0,0 +1,76 @@ +#include "tetramino.h" + +Tetramino::Tetramino(QObject *parent) : QObject(parent) {} + +Tetramino::~Tetramino() {} + +void Tetramino::SetRandomFigure() { + int countFigures = 7; + int randNumb = 1 + rand() % countFigures; + switch (randNumb) { + case 1: + currentFigure_ = { + {1}, + {1}, + {1}, + {1} + }; + colorFigure_ = Qt::cyan; + break; + case 2: + currentFigure_ = { + {0, 1}, + {0, 1}, + {1, 1} + }; + colorFigure_ = Qt::magenta; + break; + case 3: + currentFigure_ = { + {1, 0}, + {1, 0}, + {1, 1} + }; + colorFigure_ = Qt::blue; + break; + case 4: + currentFigure_ = { + {1, 1}, + {1, 1} + }; + colorFigure_ = Qt::yellow; + break; + case 5: + currentFigure_ = { + {1, 1, 0}, + {0, 1, 1} + }; + colorFigure_ = Qt::green; + break; + case 6: + currentFigure_ = { + {0, 1, 0}, + {1, 1, 1} + }; + colorFigure_ = Qt::gray; + break; + case 7: + currentFigure_ = { + {0, 1, 1}, + {1, 1, 0} + }; + colorFigure_ = Qt::red; + break; + default: + break; + } +} + +QVector> Tetramino::GetCurrentFigure() { + SetRandomFigure(); + return currentFigure_; +} + +QColor Tetramino::GetColorFigure() { + return colorFigure_; +} diff --git a/tetramino.h b/tetramino.h new file mode 100644 index 0000000..8a5d811 --- /dev/null +++ b/tetramino.h @@ -0,0 +1,22 @@ +#ifndef TETRAMINO_H +#define TETRAMINO_H + +#include +#include +#include + +class Tetramino : public QObject { + Q_OBJECT +public: + explicit Tetramino(QObject *parent = nullptr); + ~Tetramino(); + void SetRandomFigure(); + + QVector> GetCurrentFigure(); + QColor GetColorFigure(); + +private: + QVector> currentFigure_; + QColor colorFigure_; +}; +#endif // TETRAMINO_H diff --git a/tetris.cpp b/tetris.cpp index 50cf380..f53328d 100644 --- a/tetris.cpp +++ b/tetris.cpp @@ -1,9 +1,151 @@ #include "tetris.h" #include "ui_tetris.h" +#include +#include +#include +#include +#include Tetris::Tetris(QWidget *parent) : QMainWindow(parent), ui(new Ui::Tetris) { - ui->setupUi(this); + ui->setupUi(this); + + baseSpeed_ = 400; + boostSpeed_ = 10; + + this->setMinimumSize(1200, 800); + this->setMaximumSize(1200, 800); + + scoreCounter_ = new QLCDNumber; + scoreCounter_->setFixedSize(200, 50); + scoreCounter_->setDigitCount(6); + scoreCounter_->display("000000"); + + nextFigureGrid_ = new NextFigureGameGrid(this); + gamefield_ = new GameField(this); + + gamefield_->setFixedSize(601, 721); + + QPushButton *newGameButton = new QPushButton("Новая игра"); + + QGridLayout *gridLayout = new QGridLayout; + gridLayout->addWidget(nextFigureGrid_, 1, 0, Qt::AlignHCenter); + gridLayout->addWidget(gamefield_, 1, 1, Qt::AlignHCenter); + gridLayout->addWidget(scoreCounter_, 1, 2, Qt::AlignHCenter); + gridLayout->addWidget(newGameButton, 0, 1, Qt::AlignHCenter); + + setCentralWidget(new QWidget(this)); + centralWidget()->setLayout(gridLayout); + + timer_ = new QTimer(this); + + connect(newGameButton, &QPushButton::clicked, nextFigureGrid_, &NextFigureGameGrid::SetNextFigure); + connect(newGameButton, &QPushButton::clicked, this, &Tetris::UpdateGameFieldFigure); + connect(newGameButton, &QPushButton::clicked, this, &Tetris::StartNewGame); + connect(gamefield_, &GameField::FigureSpawned, nextFigureGrid_, &NextFigureGameGrid::SetNextFigure); + connect(nextFigureGrid_, &NextFigureGameGrid::NextFigureChanged, this, &Tetris::UpdateGameFieldFigure); + connect(timer_, &QTimer::timeout, gamefield_, &GameField::MoveFigure); + connect(gamefield_, &GameField::FigureSpawned, this, &Tetris::RestoreBasedInterval); + connect(gamefield_, &GameField::CurrentFigureChanged, this, &Tetris::UpdateGameField); + connect(gamefield_, &GameField::ChangeWinPoints, this, &Tetris::UpdateWinPoints); + connect(gamefield_, &GameField::GameOver, this, &Tetris::OpenEndGameDialog); } Tetris::~Tetris() { delete ui; } + +void Tetris::UpdateGameField() { + gamefield_->update(); +} + +void Tetris::UpdateGameFieldFigure() { + gamefield_->SetNextFigureGrid(nextFigureGrid_->GetNextFigure()); +} + +void Tetris::UpdateWinPoints() { + int currentCountPoints = gamefield_->GetWinPoints(); + QString newDisplayValue = QString::number(currentCountPoints).rightJustified(scoreCounter_->digitCount(), '0'); + scoreCounter_->display(newDisplayValue); +} + +void Tetris::RestoreBasedInterval() { + if (timer_->interval() != baseSpeed_) { + timer_->setInterval(baseSpeed_); + timer_->start(); + } +} + +void Tetris::GamePause() { + timer_->stop(); + OpenModalDialog(); +} + +void Tetris::StartNewGame() { + int CenterPointX = 2; + timer_->stop(); + gamefield_->SetWinPoints(0); + UpdateWinPoints(); + + gamefield_->ClearGameGrid(); + gamefield_->SpawnNextFigure(); + gamefield_->SetFigurePosition(0, gamefield_->GetColumnsNumber() / CenterPointX); + + gamefield_->setFocus(); + timer_->start(baseSpeed_); +} + +void Tetris::EndGame() { + timer_->stop(); + gamefield_->ClearGameGrid(); + gamefield_->SetWinPoints(0); +} + +void Tetris::OpenModalDialog() { + ModalDialog dialog(this); + dialog.exec(); + timer_->start(400); +} + +void Tetris::OpenEndGameDialog() { + GameOverDialog dialog(this); + dialog.exec(); + EndGame(); +} + +void Tetris::keyPressEvent(QKeyEvent *event) { + int currRow = gamefield_->GetCurrentFigureRow(); + int currColumn = gamefield_->GetCurrentFigureColumn(); + + if (event->key() == Qt::Key_Space) { + GamePause(); + } + + if (event->key() == Qt::Key_Right) { + if(!gamefield_->HasCollisionMove(0, 1)) { + gamefield_->SetFigurePosition(currRow, currColumn + 1); + } + } + + if (event->key() == Qt::Key_Left) { + if(!gamefield_->HasCollisionMove(0, -1)) { + gamefield_->SetFigurePosition(currRow, currColumn - 1); + } + } + + if (event->key() == Qt::Key_Down) { + timer_->start(boostSpeed_); + } + + if (event->key() == Qt::Key_Up) { + gamefield_->GetRotateCurrentFigure(); + + QVector> CurrentFigure = gamefield_->GetCurrentFigure(); + + if(!gamefield_->HasCollisionRotation()) { + gamefield_->GetCurrentFigure(); + gamefield_->update(); + } else { + gamefield_->SetFigurePosition(currRow, currColumn - CurrentFigure[0].size() + 1); + gamefield_->update(); + } + } +} diff --git a/tetris.h b/tetris.h index 1245a26..3bb481b 100644 --- a/tetris.h +++ b/tetris.h @@ -2,6 +2,13 @@ #define TETRIS_H #include +#include +#include "gamefield.h" +#include "modaldialog.h" +#include "gameoverdialog.h" +#include "nextfiguregamegrid.h" + +#include QT_BEGIN_NAMESPACE namespace Ui { @@ -16,7 +23,27 @@ class Tetris : public QMainWindow { Tetris(QWidget *parent = nullptr); ~Tetris(); - private: - Ui::Tetris *ui; + void GamePause(); + void RestoreBasedInterval(); + void UpdateGameField(); + void UpdateWinPoints(); + void StartNewGame(); + void EndGame(); + void OpenModalDialog(); + void OpenEndGameDialog(); + +private slots: + void UpdateGameFieldFigure(); + +protected: + void keyPressEvent(QKeyEvent *event); +private: + Ui::Tetris *ui; + + int baseSpeed_, boostSpeed_; + QTimer *timer_; + GameField *gamefield_; + QLCDNumber *scoreCounter_; + NextFigureGameGrid *nextFigureGrid_; }; #endif // TETRIS_H