Documentos de Académico
Documentos de Profesional
Documentos de Cultura
In this part of the Qt4 C++ programming tutorial we will create a simple Breakout
game clone.
The Breakout is an arcade game developed by Atari Inc. The game was created in
1976. In this game, the player moves a paddle on the screen and bounces a ball/balls.
The objective is to destroy bricks in the top of the window.
The development
In our game, we have one paddle, one ball and 30 bricks. I have created an image for
a ball, paddle and a brick in Inkscape. We use a timer to create a game cycle. We do
not work with angles, we simply change directions. Top, bottom, left and right. I was
inspired by the pybreakout game. It was developed in PyGame library by Nathan
Dawson.
The game is intentionally simple. There are no bonuses, levels, score. So that it is
easier to understand.
The Qt4 C++ programming library is developed for creating computer applications.
Nevertheless, it can be used to create games as well. Developing a computer game is
a great way to learn the Qt4 programming library.
paddle.h
#ifndef PADDLE_H
#define PADDLE_H
#include <QImage>
#include <QRect>
class Paddle
public:
Paddle();
~Paddle();
public:
void resetState();
void moveLeft(int);
void moveRight(int);
QRect getRect();
private:
QImage image;
QRect rect;
};
#endif
paddle.cpp
#include "paddle.h"
Paddle::Paddle()
image.load("paddle.png");
rect = image.rect();
resetState();
Paddle::~Paddle()
printf("paddle deleted\n");
if (rect.left() >= 2)
rect.moveTo(left, rect.top());
rect.moveTo(right, rect.top());
void Paddle::resetState()
rect.moveTo(200, 360);
QRect Paddle::getRect()
return rect;
}
QImage & Paddle::getImage()
return image;
Paddle::Paddle()
image.load("paddle.png");
rect = image.rect();
resetState();
In the constructor, we load our paddle image. Get the image rectangle and move the
image to it's starting position.
if (rect.left() >= 2)
rect.moveTo(left, rect.top());
}
The moveLeft() method moves the rectangle to the left.
brick.h
#ifndef BRICK_H
#define BRICK_H
#include <QImage>
#include <QRect>
class Brick
public:
Brick(int, int);
~Brick();
public:
void resetState();
bool isDestroyed();
void setDestroyed(bool);
QRect getRect();
void setRect(QRect);
private:
QImage image;
QRect rect;
int position;
bool destroyed;
};
#endif
brick.cpp
#include "brick.h"
Brick::Brick(int x, int y)
image.load("brickie.png");
destroyed = FALSE;
rect = image.rect();
rect.translate(x, y);
Brick::~Brick() {
printf("Brick deleted\n");
QRect Brick::getRect()
return rect;
rect = rct;
return image;
bool Brick::isDestroyed()
return destroyed;
destroyed = destr;
bool Brick::isDestroyed()
return destroyed;
The brick has a destroyed flag. If the destroyed flag is set, the brick is not drawn on
the window.
ball.h
#ifndef BALL_H
#define BALL_H
#include <QImage>
#include <QRect>
class Ball
public:
Ball();
~Ball();
public:
void resetState();
void moveBottom(int);
void moveTop(int);
void moveLeft(int);
void moveRight(int);
void autoMove();
void setXDir(int);
void setYDir(int);
int getXDir();
int getYDir();
QRect getRect();
private:
int angle;
int speed;
int xdir;
int ydir;
bool stuck;
QImage image;
QRect rect;
};
#endif
ball.cpp
#include "ball.h"
Ball::Ball()
xdir = 1;
ydir = -1;
image.load("ball.png");
rect = image.rect();
resetState();
Ball::~Ball() {
printf("Ball deleted\n");
}
void Ball::autoMove()
rect.translate(xdir, ydir);
if (rect.left() == 0) {
xdir = 1;
if (rect.right() == 300) {
xdir = -1;
if (rect.top() == 0) {
ydir = 1;
void Ball::resetState()
rect.moveTo(230, 355);
}
rect.moveBottom(bottom);
rect.moveTop(top);
rect.moveLeft(left);
rect.moveRight(right);
}
void Ball::setXDir(int x)
xdir = x;
void Ball::setYDir(int y)
ydir = y;
int Ball::getXDir()
return xdir;
int Ball::getYDir()
return ydir;
}
QRect Ball::getRect()
return rect;
return image;
The autoMove() method is called each game cycle to move the ball on the screen. If
it hists the boudaries, the ball direction changes.
breakout.h
#ifndef BREAKOUT_H
#define BREAKOUT_H
#include "ball.h"
#include "brick.h"
#include "paddle.h"
#include <QWidget>
#include <QKeyEvent>
class Breakout : public QWidget
Q_OBJECT
public:
~Breakout();
protected:
void startGame();
void pauseGame();
void stopGame();
void victory();
void checkCollision();
private:
int x;
int timerId;
Ball *ball;
Paddle *paddle;
Brick * bricks[30];
bool gameOver;
bool gameWon;
bool gameStarted;
bool paused;
};
#endif
int x;
int timerId;
The x value stores the current x position of the paddle. The timerId is used for
identifying of the timer object. This is necessary, when we pause the game.
breakout.cpp
#include "breakout.h"
#include <QPainter>
#include <QApplication>
Breakout::Breakout(QWidget *parent)
: QWidget(parent)
x = 0;
gameOver = FALSE;
gameWon = FALSE;
paused = FALSE;
gameStarted = FALSE;
int k = 0;
k++;
}
Breakout::~Breakout() {
delete ball;
delete paddle;
delete bricks[i];
QPainter painter(this);
if (gameOver) {
QFontMetrics fm(font);
int h = height();
int w = width();
painter.translate(QPoint(w/2, h/2));
else if(gameWon) {
QFontMetrics fm(font);
painter.setFont(font);
int h = height();
int w = width();
painter.translate(QPoint(w/2, h/2));
painter.drawText(-textWidth/2, 0, "Victory");
else {
painter.drawImage(ball->getRect(),
ball->getImage());
painter.drawImage(paddle->getRect(),
paddle->getImage());
if (!bricks[i]->isDestroyed())
painter.drawImage(bricks[i]->getRect(),
bricks[i]->getImage());
ball->autoMove();
checkCollision();
repaint();
}
void Breakout::keyPressEvent(QKeyEvent *event)
switch (event->key()) {
case Qt::Key_Left:
int x = paddle->getRect().x();
paddle->moveLeft(x--);
break;
case Qt::Key_Right:
int x = paddle->getRect().x();
paddle->moveRight(x++);
break;
case Qt::Key_P:
pauseGame();
}
break;
case Qt::Key_Space:
startGame();
break;
case Qt::Key_Escape:
qApp->exit();
break;
default:
QWidget::keyPressEvent(event);
void Breakout::startGame()
if (!gameStarted) {
ball->resetState();
paddle->resetState();
for (int i=0; i<30; i++) {
bricks[i]->setDestroyed(FALSE);
gameOver = FALSE;
gameWon = FALSE;
gameStarted = TRUE;
timerId = startTimer(10);
void Breakout::pauseGame()
if (paused) {
timerId = startTimer(10);
paused = FALSE;
} else {
paused = TRUE;
killTimer(timerId);
}
void Breakout::stopGame()
killTimer(timerId);
gameOver = TRUE;
gameStarted = FALSE;
void Breakout::victory()
killTimer(timerId);
gameWon = TRUE;
gameStarted = FALSE;
void Breakout::checkCollision()
stopGame();
for (int i=0, j=0; i<30; i++) {
if (bricks[i]->isDestroyed()) {
j++;
if (j==30)
victory();
if ((ball->getRect()).intersects(paddle->getRect())) {
ball->setXDir(-1);
ball->setYDir(-1);
}
ball->setXDir(-1);
ball->setYDir(-1*ball->getYDir());
ball->setXDir(0);
ball->setYDir(-1);
ball->setXDir(1);
ball->setYDir(-1*ball->getYDir());
ball->setXDir(1);
ball->setYDir(-1);
}
}
if ((ball->getRect()).intersects(bricks[i]->getRect())) {
if (!bricks[i]->isDestroyed()) {
if(bricks[i]->getRect().contains(pointRight)) {
ball->setXDir(-1);
}
else if(bricks[i]->getRect().contains(pointLeft)) {
ball->setXDir(1);
if(bricks[i]->getRect().contains(pointTop)) {
ball->setYDir(1);
else if(bricks[i]->getRect().contains(pointBottom)) {
ball->setYDir(-1);
bricks[i]->setDestroyed(TRUE);
k++;
painter.drawImage(ball->getRect(),
ball->getImage());
painter.drawImage(paddle->getRect(),
paddle->getImage());
if (!bricks[i]->isDestroyed())
painter.drawImage(bricks[i]->getRect(),
bricks[i]->getImage());
}
If the game is not won or lost, we draw the ball, paddle and the bricks in
the paintEvent().
ball->autoMove();
checkCollision();
repaint();
In the timerEvent(), we move the ball, check if the ball collided with the paddle or a
brick and generate a paint event.
case Qt::Key_Left:
int x = paddle->getRect().x();
paddle->moveLeft(x--);
break;
If we press the left cursor key, we call the moveLeft() method of the paddle object.
We call the method five times, so that the movement is smooth.
void Breakout::stopGame()
killTimer(timerId);
gameOver = TRUE;
gameStarted = FALSE;
In the stopGame() method, we kill the timer and set the appropriate flags.
stopGame();
if (bricks[i]->isDestroyed()) {
j++;
if (j==30)
victory();
We check how many bricks are destroyed. If we destroyed all 30 bricks, we win the
game.
if (ballLPos < first) {
ball->setXDir(-1);
ball->setYDir(-1);
If the ball hits the first part of the paddle, we change the direction of the ball to north
east.
if(bricks[i]->getRect().contains(pointTop)) {
ball->setYDir(1);
If the ball hits the bottom of the brick, we change the y direction of the ball. It goes
down.
Figure: The Breakout game