常量头文件
constants.h
#ifndef CONSTANTS_H
#define CONSTANTS_H
const int TILE_SIZE = 10;
enum GameObjectsData {
GD_Type
};
enum GameObjectTypes {
GO_Food,
GO_Wall
};
#endif // CONSTANTS_H
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class QGraphicsScene;
class QGraphicsView;
class GameController;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void adjustViewSize();
void newGame();
void gameHelp();
void about();
private:
void createActions();
void createMenus();
void initScene();
void initSceneBackground();
QGraphicsScene *scene;
QGraphicsView *view;
GameController *game;
QAction *newGameAction;
QAction *pauseAction;
QAction *resumeAction;
QAction *exitAction;
QAction *gameHelpAction;
QAction *aboutAction;
QAction *aboutQtAction;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include <QGraphicsView>
#include <QTimer>
#include <qaction.h>
#include <qmenubar.h>
#include <qapplication.h>
#include <qmessagebox.h>
#include "constants.h"
#include "gamecontroller.h"
#include "mainwindow.h"
#include <QIcon>
//窗体类 : 继承多个
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
scene(new QGraphicsScene(this)),
view(new QGraphicsView(scene, this)),
game(new GameController(*scene, this))
{
//窗体类的主体
setCentralWidget(view);
// resize(600, 600);
setFixedSize(600, 600);
// setWindowIcon(QIcon(":/images/snake_ico"));
createActions();
createMenus();
initScene();
initSceneBackground();
//延时睡眠
QTimer::singleShot(0, this, SLOT(adjustViewSize()));
}
//默认的析构函数
MainWindow::~MainWindow()
{
}
void MainWindow::adjustViewSize()
{
view->fitInView(scene->sceneRect(), Qt::KeepAspectRatioByExpanding);
}
void MainWindow::createActions()
{
newGameAction = new QAction(tr("&New Game"), this);
newGameAction->setShortcuts(QKeySequence::New);
newGameAction->setStatusTip(tr("Start a new game"));
connect(newGameAction, &QAction::triggered, this, &MainWindow::newGame);
exitAction = new QAction(tr("&Exit"), this);
exitAction->setShortcut(tr("Ctrl+Q"));
exitAction->setStatusTip(tr("Exit the game"));
connect(exitAction, &QAction::triggered, this, &MainWindow::close);
pauseAction = new QAction(tr("&Pause"), this);
pauseAction->setStatusTip(tr("Pause..."));
connect(pauseAction, &QAction::triggered, game, &GameController::pause);
resumeAction = new QAction(tr("&Resume"), this);
resumeAction->setStatusTip(tr("Resume..."));
connect(resumeAction, &QAction::triggered, game, &GameController::resume);
gameHelpAction = new QAction(tr("Game &Help"), this);
gameHelpAction->setShortcut(tr("Ctrl+H"));
gameHelpAction->setStatusTip(tr("Help on this game"));
connect(gameHelpAction, &QAction::triggered, this, &MainWindow::gameHelp);
aboutAction = new QAction(tr("&About"), this);
aboutAction->setStatusTip(tr("Show the application's about box"));
connect(aboutAction, &QAction::triggered, this, &MainWindow::about);
aboutQtAction = new QAction(tr("About &Qt"), this);
aboutQtAction->setStatusTip(tr("Show the Qt library's About box"));
connect(aboutQtAction, &QAction::triggered, qApp, QApplication::aboutQt);
}
//显示菜单
void MainWindow::createMenus()
{
QMenu *options = menuBar()->addMenu(tr("&Options"));
options->addAction(newGameAction);
options->addSeparator();
options->addAction(pauseAction);
options->addAction(resumeAction);
options->addSeparator();
options->addAction(exitAction);
QMenu *help = menuBar()->addMenu(tr("&Help"));
help->addAction(gameHelpAction);
help->addAction(aboutAction);
help->addAction(aboutQtAction);
}
//初始化界面
void MainWindow::initScene()
{
scene->setSceneRect(-100, -100, 200, 200);
}
void MainWindow::initSceneBackground()
{
QPixmap bg(TILE_SIZE, TILE_SIZE);
QPainter p(&bg);
p.setBrush(QBrush(Qt::gray));
p.drawRect(0, 0, TILE_SIZE, TILE_SIZE);
view->setBackgroundBrush(QBrush(bg));
}
void MainWindow::newGame()
{
QTimer::singleShot(0, game, SLOT(gameOver()));
}
void MainWindow::about()
{
QMessageBox::about(this, tr("About this Game"), tr("<h2>Snake Game</h2>"
"<p>Copyright © XXX."
"<p>This game is a small Qt application. It is based on the demo in the GitHub written by Devbean."));
}
void MainWindow::gameHelp()
{
QMessageBox::about(this, tr("Game Help"), tr("Using direction keys to control the snake to eat the food"
"<p>Space - pause & resume"));
}
gamecontroller.h
#ifndef GAMECONTROLLER_H
#define GAMECONTROLLER_H
#include <QObject>
#include <QTimer>
class QGraphicsScene;
class QKeyEvent;
class Snake;
class Food;
class Wall;
class GameController : public QObject
{
Q_OBJECT
public:
GameController(QGraphicsScene &scene, QObject *parent = 0);
~GameController();
void snakeAteFood(Food *food);
// void snakeHitWall(Snake *snake, Wall *wall);
void snakeAteItself();
public slots:
void pause();
void resume();
void gameOver();
protected:
bool eventFilter(QObject *object, QEvent *event);
private:
void handleKeyPressed(QKeyEvent *event);
void addNewFood();
QTimer timer;
QGraphicsScene &scene;
Snake *snake;
bool isPause;
};
#endif // GAMECONTROLLER_H
gamecontroller.cpp
#include <QEvent>
#include <QGraphicsScene>
#include <QKeyEvent>
#include <QMessageBox>
#include "gamecontroller.h"
#include "food.h"
#include "snake.h"
GameController::GameController(QGraphicsScene &scene, QObject *parent) :
QObject(parent),
scene(scene),
snake(new Snake(*this)),
isPause(false)
{
timer.start( 1000/33 );
Food *a1 = new Food(0, -50);
scene.addItem(a1);
scene.addItem(snake);
scene.installEventFilter(this);
resume();
}
GameController::~GameController()
{
}
void GameController::snakeAteFood(Food *food)
{
scene.removeItem(food);
addNewFood();
}
//void GameController::snakeHitWall(Snake *snake, Wall *wall)
//{
//}
void GameController::snakeAteItself()
{
QTimer::singleShot(0, this, SLOT(gameOver()));
}
void GameController::handleKeyPressed(QKeyEvent *event)
{
if (!isPause)
switch (event->key()) {
case Qt::Key_Left:
snake->setMoveDirection(Snake::MoveLeft);
break;
case Qt::Key_Right:
snake->setMoveDirection(Snake::MoveRight);
break;
case Qt::Key_Up:
snake->setMoveDirection(Snake::MoveUp);
break;
case Qt::Key_Down:
snake->setMoveDirection(Snake::MoveDown);
break;
case Qt::Key_Space:
pause();
break;
}
else resume();
}
void GameController::addNewFood()
{
int x, y;
do {
x = (int)(qrand() % 200) / 10 - 10;
y = (int)(qrand() % 200) / 10 - 10;
x *= 10;
y *= 10;
} while (snake->shape().contains(snake->mapFromScene(QPointF(x + 5, y + 5))));
Food *food = new Food(x, y);
scene.addItem(food);
}
void GameController::gameOver()
{
disconnect(&timer, SIGNAL(timeout()), &scene, SLOT(advance()));
if (QMessageBox::Yes == QMessageBox::information(NULL,
tr("Game Over"), tr("Again?"),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::Yes)) {
connect(&timer, SIGNAL(timeout()), &scene, SLOT(advance()));
scene.clear();
snake = new Snake(*this);
scene.addItem(snake);
addNewFood();
} else {
exit(0);
}
}
void GameController::pause()
{
disconnect(&timer, SIGNAL(timeout()),
&scene, SLOT(advance()));
isPause = true;
}
void GameController::resume()
{
connect(&timer, SIGNAL(timeout()),
&scene, SLOT(advance()));
isPause = false;
}
bool GameController::eventFilter(QObject *object, QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
handleKeyPressed((QKeyEvent *)event);
return true;
} else {
return QObject::eventFilter(object, event);
}
}
snake.h
#ifndef SNAKE_H
#define SNAKE_H
#include <QGraphicsItem>
#include <QRectF>
class GameController;
class Snake : public QGraphicsItem
{
public:
enum Direction {
NoMove,
MoveLeft,
MoveRight,
MoveUp,
MoveDown
};
Snake(GameController & controller);
QRectF boundingRect() const;
QPainterPath shape() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *);
void setMoveDirection(Direction direction);
Direction currentDirection();
protected:
void advance(int step);
private:
void moveLeft();
void moveRight();
void moveUp();
void moveDown();
void handleCollisions();
QPointF head;
int growing;
int speed;
QList<QPointF> tail;
int tickCounter;
Direction moveDirection;
GameController &controller;
};
#endif // SNAKE_H
snake.cpp
#include <QPainter>
#include "constants.h"
#include "gamecontroller.h"
#include "snake.h"
static const qreal SNAKE_SIZE = TILE_SIZE;
Snake::Snake(GameController &controller) :
head(0, 0),
growing(7),
speed(5),
moveDirection(NoMove),
controller(controller)
{
}
QRectF Snake::boundingRect() const
{
qreal minX = head.x();
qreal minY = head.y();
qreal maxX = head.x();
qreal maxY = head.y();
foreach (QPointF p, tail) {
maxX = p.x() > maxX ? p.x() : maxX;
maxY = p.y() > maxY ? p.y() : maxY;
minX = p.x() < minX ? p.x() : minX;
minY = p.y() < minY ? p.y() : minY;
}
QPointF tl = mapFromScene(QPointF(minX, minY));
QPointF br = mapFromScene(QPointF(maxX, maxY));
QRectF bound = QRectF(tl.x(), // x
tl.y(), // y
br.x() - tl.x() + SNAKE_SIZE, // width
br.y() - tl.y() + SNAKE_SIZE //height
);
return bound;
}
QPainterPath Snake::shape() const
{
QPainterPath path;
path.setFillRule(Qt::WindingFill);
path.addRect(QRectF(0, 0, SNAKE_SIZE, SNAKE_SIZE));
foreach (QPointF p, tail) {
QPointF itemp = mapFromScene(p);
path.addRect(QRectF(itemp.x(), itemp.y(), SNAKE_SIZE, SNAKE_SIZE));
}
return path;
}
void Snake::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
painter->save();
painter->fillPath(shape(), Qt::yellow);
painter->restore();
}
void Snake::setMoveDirection(Direction direction)
{
if (moveDirection == MoveLeft && direction == MoveRight)
return;
if (moveDirection == MoveRight && direction == MoveLeft)
return;
if (moveDirection == MoveUp && direction == MoveDown)
return;
if (moveDirection == MoveDown && direction == MoveUp)
return;
moveDirection = direction;
}
Snake::Direction Snake::currentDirection()
{
return moveDirection;
}
void Snake::advance(int step)
{
if (!step) {
return;
}
if (tickCounter++ % speed != 0) {
return;
}
if (moveDirection == NoMove) {
return;
}
if (growing > 0) {
QPointF tailPoint = head;
tail << tailPoint;
growing -= 1;
} else {
tail.removeFirst();
tail << head;
}
switch (moveDirection) {
case MoveLeft:
moveLeft();
break;
case MoveRight:
moveRight();
break;
case MoveUp:
moveUp();
break;
case MoveDown:
moveDown();
break;
}
setPos(head);
handleCollisions();
}
void Snake::moveLeft()
{
head.rx() -= SNAKE_SIZE;
if (head.rx() < -100) {
head.rx() = 90;
}
}
void Snake::moveRight()
{
head.rx() += SNAKE_SIZE;
if (head.rx() >= 100) {
head.rx() = -100;
}
}
void Snake::moveUp()
{
head.ry() -= SNAKE_SIZE;
if (head.ry() < -100) {
head.ry() = 90;
}
}
void Snake::moveDown()
{
head.ry() += SNAKE_SIZE;
if (head.ry() >= 100) {
head.ry() = -100;
}
}
void Snake::handleCollisions()
{
QList<QGraphicsItem *> collisions = collidingItems();
// Check collisions with other objects on screen
foreach (QGraphicsItem *collidingItem, collisions) {
if (collidingItem->data(GD_Type) == GO_Food) {
// Let GameController handle the event by putting another apple
controller.snakeAteFood((Food *)collidingItem);
growing += 1;
}
}
// Check snake eating itself
if (tail.contains(head)) {
controller.snakeAteItself();
}
}
food.h
#ifndef FOOD_H
#define FOOD_H
#include <QGraphicsItem>
class Food : public QGraphicsItem
{
public:
Food(qreal x, qreal y);
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *);
QPainterPath shape() const;
};
#endif // FOOD_H
food.cpp
#include <QPainter>
#include "constants.h"
#include "food.h"
static const qreal FOOD_RADIUS = 3.0;
Food::Food(qreal x, qreal y)
{
setPos(x, y);
setData(GD_Type, GO_Food);
}
QRectF Food::boundingRect() const
{
return QRectF(-TILE_SIZE, -TILE_SIZE,
TILE_SIZE * 2, TILE_SIZE * 2 );
}
void Food::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
painter->fillPath(shape(), Qt::red);
painter->restore();
}
QPainterPath Food::shape() const
{
QPainterPath p;
p.addEllipse(QPointF(TILE_SIZE / 2, TILE_SIZE / 2), FOOD_RADIUS, FOOD_RADIUS);
return p;
}
wall.h
#ifndef WALL_H
#define WALL_H
#include <QGraphicsItem>
class Wall : public QGraphicsItem
{
public:
Wall();
};
#endif // WALL_H
wall.cpp
#include "wall.h"
Wall::Wall()
{
}