以下是一个简单的俄罗斯方块游戏的 QT C++ 实现的基本思路:
一、解决思路:
- 创建项目:使用 QT Creator 创建一个新的 QT Widgets Application 项目。
- 设计界面:使用 QT Designer 设计游戏界面,包括游戏区域(显示方块下落的区域)、分数显示区域、下一个方块的预览区域等。添加开始、暂停、结束等按钮。
- 方块类:创建一个 Tetromino 类,用于表示不同形状的俄罗斯方块,存储方块的形状信息和旋转状态。实现方块的旋转、移动等操作。
- 游戏区域类:创建一个 GameBoard 类,负责处理游戏区域的逻辑,包括存储已落下的方块信息。实现碰撞检测,判断方块是否可以继续下落或移动。处理方块到达底部后的固定操作,更新游戏区域状态。
- 游戏逻辑类:创建一个 GameLogic 类,负责游戏的主要逻辑,如生成新的方块,控制方块的下落速度,更新分数。实现游戏的开始、暂停、结束等状态控制。
- 定时器类:使用 QT 的 QTimer 类,用于控制方块的下落时间间隔。
二、代码实现:
收起
cpp
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// 表示俄罗斯方块的形状
class Tetromino {
public:
enum Shape {
NoShape, ZShape, SShape, LineShape, TShape, SquareShape, LShape, MirroredLShape
};
Tetromino() {
setShape(NoShape);
}
void setShape(Shape shape) {
static const int coordsTable[8][4][2] = {
{{0, 0}, {0, 0}, {0, 0}, {0, 0}},
{{0, -1}, {0, 0}, {-1, 0}, {-1, 1}},
{{0, -1}, {0, 0}, {1, 0}, {1, 1}},
{{0, -1}, {0, 0}, {0, 1}, {0, 2}},
{{-1, 0}, {0, 0}, {1, 0}, {0, 1}},
{{0, 0}, {1, 0}, {0, 1}, {1, 1}},
{{-1, -1}, {0, -1}, {0, 0}, {0, 1}},
{{1, -1}, {0, -1}, {0, 0}, {0, 1}}
};
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 2; ++j) {
coords[i][j] = coordsTable[shape][i][j];
}
}
pieceShape = shape;
}
Shape shape() const { return pieceShape; }
int x(int index) const { return coords[index][0]; }
int y(int index) const { return coords[index][1]; }
void rotateLeft() {
if (pieceShape == SquareShape)
return;
for (int i = 0; i < 4; ++i) {
int x = coords[i][1];
coords[i][1] = -coords[i][0];
coords[i][0] = x;
}
}
void rotateRight() {
if (pieceShape == SquareShape)
return;
for (int i = 0; i < 4; ++i) {
int x = -coords[i][1];
coords[i][1] = coords[i][0];
coords[i][0] = x;
}
}
private:
Shape pieceShape;
int coords[4][2];
};
// 游戏区域类
class GameBoard : public QWidget {
public:
GameBoard(QWidget *parent = nullptr) : QWidget(parent) {
setFixedSize(200, 400);
clearBoard();
}
void clearBoard() {
for (int i = 0; i < BoardHeight; ++i) {
for (int j = 0; j < BoardWidth; ++j) {
board[i][j] = Tetromino::NoShape;
}
}
}
bool isCollision(const Tetromino &tetromino, int x, int y) {
for (int i = 0; i < 4; ++i) {
int newX = x + tetromino.x(i);
int newY = y + tetromino.y(i);
if (newX < 0 || newX >= BoardWidth || newY >= BoardHeight)
return true;
if (newY >= 0 && board[newY][newX]!= Tetromino::NoShape)
return true;
}
return false;
}
void placeTetromino(const Tetromino &tetromino, int x, int y) {
for (int i = 0; i < 4; ++i) {
int newX = x + tetromino.x(i);
int newY = y + tetromino.y(i);
if (newY >= 0) {
board[newY][newX] = tetromino.shape();
}
}
}
bool isRowFull(int row) {
for (int i = 0; i < BoardWidth; ++i) {
if (board[row][i] == Tetromino::NoShape)
return false;
}
return true;
}
void removeFullRows() {
for (int i = BoardHeight - 1; i >= 0; --i) {
if (isRowFull(i)) {
for (int k = i; k > 0; --k) {
for (int j = 0; j < BoardWidth; ++j) {
board[k][j] = board[k - 1][j];
}
}
for (int j = 0; j < BoardWidth; ++j) {
board[0][j] = Tetromino::NoShape;
}
}
}
}
void paintEvent(QPaintEvent *event) override {
QPainter painter(this);
for (int i = 0; i < BoardHeight; ++i) {
for (int j = 0; j < BoardWidth; ++j) {
if (board[i][j]!= Tetromino::NoShape) {
drawSquare(painter, j * squareWidth(), i * squareHeight(), getColor(board[i][j]));
}
}
}
}
private:
void drawSquare(QPainter &painter, int x, int y, QColor color) {
painter.fillRect(x + 1, y + 1, squareWidth() - 2, squareHeight() - 2, color);
painter.setPen(QPen(Qt::black, 1));
painter.drawRect(x, y, squareWidth(), squareHeight());
}
QColor getColor(Tetromino::Shape shape) {
switch (shape) {
case Tetromino::ZShape:
return Qt::red;
case Tetromino::SShape:
return Qt::green;
case Tetromino::LineShape:
return Qt::blue;
case Tetromino::TShape:
return Qt::yellow;
case Tetromino::SquareShape:
return Qt::magenta;
case Tetromino::LShape:
return Qt::cyan;
case Tetromino::MirroredLShape:
return Qt::darkYellow;
default:
return Qt::white;
}
}
static constexpr int BoardWidth = 10;
static constexpr int BoardHeight = 20;
static constexpr int squareWidth() { return 20; }
static constexpr int squareHeight() { return 20; }
Tetromino::Shape board[BoardHeight][BoardWidth];
};
// 游戏逻辑类
class GameLogic : public QWidget {
public:
GameLogic(QWidget *parent = nullptr) : QWidget(parent) {
gameBoard = new GameBoard;
nextPiece = new GameBoard;
scoreLabel = new QLabel("Score: 0");
startButton = new QPushButton("Start");
pauseButton = new QPushButton("Pause");
endButton = new QPushButton("End");
connect(startButton, &QPushButton::clicked, this, &GameLogic::start);
connect(pauseButton, &QPushButton::clicked, this, &GameLogic::pause);
connect(endButton, &QPushButton::clicked, this, &GameLogic::end);
QVBoxLayout *mainLayout = new QVBoxLayout;
QHBoxLayout *boardLayout = new QHBoxLayout;
boardLayout->addWidget(gameBoard);
boardLayout->addWidget(nextPiece);
mainLayout->addLayout(boardLayout);
mainLayout->addWidget(scoreLabel);
mainLayout->addWidget(startButton);
mainLayout->addWidget(pauseButton);
mainLayout->addWidget(endButton);
setLayout(mainLayout);
timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &GameLogic::dropDown);
currentPiece.setShape(Tetromino::NoShape);
nextPieceShape.setShape(Tetromino::NoShape);
generateNewPiece();
}
void start() {
if (isPaused) {
isPaused = false;
timer->start(500);
} else {
score = 0;
gameBoard->clearBoard();
generateNewPiece();
timer->start(500);
}
}
void pause() {
if (isPaused) {
timer->start(500);
isPaused = false;
} else {
timer->stop();
isPaused = true;
}
}
void end() {
timer->stop();
QMessageBox::information(this, "Game Over", "Game Over!");
}
void dropDown() {
if (isPaused)
return;
int newX = currentX;
int newY = currentY + 1;
if (gameBoard->isCollision(currentPiece, newX, newY)) {
gameBoard->placeTetromino(currentPiece, currentX, currentY);
gameBoard->removeFullRows();
generateNewPiece();
} else {
currentY = newY;
}
update();
}
void generateNewPiece() {
currentPiece = nextPieceShape;
nextPieceShape.setShape(static_cast(QRandomGenerator::global()->bounded(1, 7)));
currentX = BoardWidth / 2;
currentY = 0;
if (gameBoard->isCollision(currentPiece, currentX, currentY)) {
end();
}
}
private:
GameBoard *gameBoard;
GameBoard *nextPiece;
QLabel *scoreLabel;
QPushButton *startButton;
QPushButton *pauseButton;
QPushButton *endButton;
QTimer *timer;
Tetromino currentPiece;
Tetromino nextPieceShape;
int currentX;
int currentY;
int score;
bool isPaused = false;
static constexpr int BoardWidth = 10;
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
GameLogic game;
game.show();
return app.exec();
}
三、代码解释:
- Tetromino 类:定义了俄罗斯方块的不同形状(Shape 枚举),存储方块的坐标和形状信息。提供了设置形状、获取形状、旋转方块(左右旋转)等方法。
- GameBoard 类:继承自 QWidget,表示游戏区域。存储了一个二维数组 board 表示游戏区域的状态,每个元素存储方块的形状。clearBoard 方法用于清空游戏区域。isCollision 方法用于检测方块是否会与已有方块或边界碰撞。placeTetromino 方法将方块放置在游戏区域。isRowFull 方法检查一行是否已满,removeFullRows 方法移除满行。paintEvent 方法使用 QPainter 绘制游戏区域的方块。
- GameLogic 类:包含游戏的主要逻辑,包括 GameBoard 和 GameBoard 用于显示下一个方块。提供开始、暂停、结束游戏的功能。使用 QTimer 控制方块的下落,通过 dropDown 方法实现方块下落的逻辑。generateNewPiece 方法生成新的方块并更新下一个方块。update 方法会触发界面的重绘。
四、使用说明:
- 创建一个新的 QT Widgets Application 项目,将上述代码复制到项目的 .cpp 文件中。
- 编译并运行程序,会看到一个简单的俄罗斯方块游戏界面。
- 点击 Start 按钮开始游戏,Pause 按钮暂停游戏,End 按钮结束游戏。
- 方块会自动下落,玩家可以通过扩展 GameLogic 类添加按键事件处理逻辑,实现方块的手动移动和旋转操作。
这个示例是一个简单的实现,还有许多可以改进和扩展的地方,例如增加难度级别、添加音效、记录最高分等