├── .gitignore ├── README.md ├── constants.h ├── food.cpp ├── food.h ├── gamecontroller.cpp ├── gamecontroller.h ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── myapp.rc ├── res.qrc ├── snake.cpp ├── snake.h ├── snake.ico ├── snake.pro ├── wall.cpp └── wall.h /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ objects and libs 2 | 3 | *.slo 4 | *.lo 5 | *.o 6 | *.a 7 | *.la 8 | *.lai 9 | *.so 10 | *.dll 11 | *.dylib 12 | 13 | # Qt-es 14 | 15 | /.qmake.cache 16 | /.qmake.stash 17 | *.pro.user 18 | *.pro.user.* 19 | *.qbs.user 20 | *.qbs.user.* 21 | *.moc 22 | moc_*.cpp 23 | qrc_*.cpp 24 | ui_*.h 25 | Makefile* 26 | *build-* 27 | 28 | # QtCreator 29 | 30 | *.autosave 31 | 32 | # QtCtreator Qml 33 | *.qmlproject.user 34 | *.qmlproject.user.* 35 | 36 | # QtCtreator CMake 37 | CMakeLists.txt.user* 38 | 39 | # folders 40 | 41 | debug/ 42 | release/ 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | snake-game 2 | ========== 3 | 4 | A snake game based on Qt. -------------------------------------------------------------------------------- /constants.h: -------------------------------------------------------------------------------- 1 | #ifndef CONSTANTS_H 2 | #define CONSTANTS_H 3 | 4 | const int TILE_SIZE = 10; 5 | 6 | enum GameObjectsData { 7 | GD_Type 8 | }; 9 | 10 | enum GameObjectTypes { 11 | GO_Food, 12 | GO_Wall 13 | }; 14 | 15 | #endif // CONSTANTS_H 16 | -------------------------------------------------------------------------------- /food.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "constants.h" 4 | #include "food.h" 5 | 6 | static const qreal FOOD_RADIUS = 3.0; 7 | 8 | Food::Food(qreal x, qreal y) 9 | { 10 | setPos(x, y); 11 | setData(GD_Type, GO_Food); 12 | } 13 | 14 | QRectF Food::boundingRect() const 15 | { 16 | return QRectF(-TILE_SIZE, -TILE_SIZE, 17 | TILE_SIZE * 2, TILE_SIZE * 2 ); 18 | } 19 | 20 | void Food::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) 21 | { 22 | painter->save(); 23 | 24 | painter->setRenderHint(QPainter::Antialiasing); 25 | painter->fillPath(shape(), Qt::red); 26 | 27 | painter->restore(); 28 | } 29 | 30 | QPainterPath Food::shape() const 31 | { 32 | QPainterPath p; 33 | p.addEllipse(QPointF(TILE_SIZE / 2, TILE_SIZE / 2), FOOD_RADIUS, FOOD_RADIUS); 34 | return p; 35 | } 36 | -------------------------------------------------------------------------------- /food.h: -------------------------------------------------------------------------------- 1 | #ifndef FOOD_H 2 | #define FOOD_H 3 | 4 | #include 5 | 6 | class Food : public QGraphicsItem 7 | { 8 | public: 9 | Food(qreal x, qreal y); 10 | 11 | QRectF boundingRect() const; 12 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *); 13 | 14 | QPainterPath shape() const; 15 | }; 16 | 17 | #endif // FOOD_H 18 | -------------------------------------------------------------------------------- /gamecontroller.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "gamecontroller.h" 9 | #include "food.h" 10 | #include "snake.h" 11 | #include "mainwindow.h" 12 | 13 | GameController::GameController(QGraphicsScene &scene, QObject *parent) : 14 | QObject(parent), 15 | scene(scene), 16 | snake(new Snake(*this)), 17 | isPause(false) 18 | { 19 | timer.start( 1000/33 ); 20 | 21 | Food *a1 = new Food(0, -50); 22 | scene.addItem(a1); 23 | 24 | scene.addItem(snake); 25 | scene.installEventFilter(this); 26 | //resume(); 27 | connect(&timer, SIGNAL(timeout()), &scene, SLOT(advance())); 28 | isPause = false; 29 | } 30 | 31 | GameController::~GameController() 32 | { 33 | } 34 | 35 | void GameController::snakeAteFood(Food *food) 36 | { 37 | scene.removeItem(food); 38 | 39 | addNewFood(); 40 | } 41 | 42 | //void GameController::snakeHitWall(Snake *snake, Wall *wall) 43 | //{ 44 | //} 45 | 46 | void GameController::snakeAteItself() 47 | { 48 | QTimer::singleShot(0, this, SLOT(gameOver())); 49 | } 50 | 51 | void GameController::handleKeyPressed(QKeyEvent *event) 52 | { 53 | if (!isPause) 54 | switch (event->key()) { 55 | case Qt::Key_Left: 56 | snake->setMoveDirection(Snake::MoveLeft); 57 | break; 58 | case Qt::Key_Right: 59 | snake->setMoveDirection(Snake::MoveRight); 60 | break; 61 | case Qt::Key_Up: 62 | snake->setMoveDirection(Snake::MoveUp); 63 | break; 64 | case Qt::Key_Down: 65 | snake->setMoveDirection(Snake::MoveDown); 66 | break; 67 | case Qt::Key_Space: 68 | pause(); 69 | break; 70 | } 71 | else resume(); 72 | } 73 | 74 | void GameController::addNewFood() 75 | { 76 | int x, y; 77 | 78 | do { 79 | x = (int)(qrand() % 200) / 10 - 10; 80 | y = (int)(qrand() % 200) / 10 - 10; 81 | 82 | x *= 10; 83 | y *= 10; 84 | } while (snake->shape().contains(snake->mapFromScene(QPointF(x + 5, y + 5)))); 85 | 86 | Food *food = new Food(x, y); 87 | scene.addItem(food); 88 | } 89 | 90 | void GameController::gameOver() 91 | { 92 | disconnect(&timer, SIGNAL(timeout()), &scene, SLOT(advance())); 93 | if (QMessageBox::Yes == QMessageBox::information(NULL, 94 | tr("Game Over"), tr("Again?"), 95 | QMessageBox::Yes | QMessageBox::No, 96 | QMessageBox::Yes)) { 97 | connect(&timer, SIGNAL(timeout()), &scene, SLOT(advance())); 98 | scene.clear(); 99 | 100 | snake = new Snake(*this); 101 | scene.addItem(snake); 102 | addNewFood(); 103 | } else { 104 | exit(0); 105 | } 106 | } 107 | 108 | void GameController::pause() 109 | { 110 | disconnect(&timer, SIGNAL(timeout()), 111 | &scene, SLOT(advance())); 112 | isPause = true; 113 | setResume(); 114 | } 115 | 116 | void GameController::resume() 117 | { 118 | connect(&timer, SIGNAL(timeout()), &scene, SLOT(advance())); 119 | isPause = false; 120 | setResume(); 121 | } 122 | void GameController :: setResume(){ 123 | if(isPause == true){ 124 | resumeAction->setEnabled(true); 125 | }else{ 126 | resumeAction->setEnabled(false); 127 | } 128 | } 129 | bool GameController::eventFilter(QObject *object, QEvent *event) 130 | { 131 | if (event->type() == QEvent::KeyPress) { 132 | handleKeyPressed((QKeyEvent *)event); 133 | return true; 134 | } else { 135 | return QObject::eventFilter(object, event); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /gamecontroller.h: -------------------------------------------------------------------------------- 1 | #ifndef GAMECONTROLLER_H 2 | #define GAMECONTROLLER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "mainwindow.h" 8 | class QGraphicsScene; 9 | class QKeyEvent; 10 | 11 | class Snake; 12 | class Food; 13 | class Wall; 14 | 15 | class GameController : public QObject 16 | { 17 | Q_OBJECT 18 | public: 19 | GameController(QGraphicsScene &scene, QObject *parent = 0); 20 | ~GameController(); 21 | 22 | void snakeAteFood(Food *food); 23 | // void snakeHitWall(Snake *snake, Wall *wall); 24 | void snakeAteItself(); 25 | QAction *getResmueAction(){ return resumeAction;} 26 | void setResumeAction(QAction* r){ resumeAction = r; } 27 | public slots: 28 | void pause(); 29 | void resume(); 30 | void gameOver(); 31 | protected: 32 | bool eventFilter(QObject *object, QEvent *event); 33 | 34 | private: 35 | void handleKeyPressed(QKeyEvent *event); 36 | void addNewFood(); 37 | void setResume(); 38 | QAction * resumeAction; 39 | QTimer timer; 40 | QGraphicsScene &scene; 41 | Snake *snake; 42 | bool isPause; 43 | }; 44 | 45 | #endif // GAMECONTROLLER_H 46 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | 8 | MainWindow w; 9 | w.show(); 10 | 11 | return a.exec(); 12 | } 13 | -------------------------------------------------------------------------------- /mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "constants.h" 9 | #include "gamecontroller.h" 10 | #include "mainwindow.h" 11 | #include 12 | 13 | MainWindow::MainWindow(QWidget *parent) 14 | : QMainWindow(parent), 15 | scene(new QGraphicsScene(this)), 16 | view(new QGraphicsView(scene, this)), 17 | game(new GameController(*scene, this)) 18 | { 19 | setCentralWidget(view); 20 | // resize(600, 600); 21 | setFixedSize(600, 600); 22 | setWindowIcon(QIcon(":/images/snake_ico")); 23 | 24 | createActions(); 25 | createMenus(); 26 | initScene(); 27 | initSceneBackground(); 28 | 29 | QTimer::singleShot(0, this, SLOT(adjustViewSize())); 30 | } 31 | 32 | MainWindow::~MainWindow() 33 | { 34 | 35 | } 36 | 37 | void MainWindow::adjustViewSize() 38 | { 39 | view->fitInView(scene->sceneRect(), Qt::KeepAspectRatioByExpanding); 40 | } 41 | 42 | void MainWindow::createActions() 43 | { 44 | newGameAction = new QAction(tr("&New Game"), this); 45 | newGameAction->setShortcuts(QKeySequence::New); 46 | newGameAction->setStatusTip(tr("Start a new game")); 47 | connect(newGameAction, &QAction::triggered, this, &MainWindow::newGame); 48 | 49 | exitAction = new QAction(tr("&Exit"), this); 50 | exitAction->setShortcut(tr("Ctrl+Q")); 51 | exitAction->setStatusTip(tr("Exit the game")); 52 | connect(exitAction, &QAction::triggered, this, &MainWindow::close); 53 | 54 | pauseAction = new QAction(tr("&Pause"), this); 55 | pauseAction->setStatusTip(tr("Pause...")); 56 | connect(pauseAction, &QAction::triggered, game, &GameController::pause); 57 | 58 | resumeAction = new QAction(tr("&Resume"), this); 59 | resumeAction->setStatusTip(tr("Resume...")); 60 | resumeAction->setEnabled(false); 61 | game->setResumeAction(resumeAction); 62 | connect(resumeAction, &QAction::triggered, game, &GameController::resume); 63 | 64 | gameHelpAction = new QAction(tr("Game &Help"), this); 65 | gameHelpAction->setShortcut(tr("Ctrl+H")); 66 | gameHelpAction->setStatusTip(tr("Help on this game")); 67 | connect(gameHelpAction, &QAction::triggered, this, &MainWindow::gameHelp); 68 | 69 | aboutAction = new QAction(tr("&About"), this); 70 | aboutAction->setStatusTip(tr("Show the application's about box")); 71 | connect(aboutAction, &QAction::triggered, this, &MainWindow::about); 72 | 73 | aboutQtAction = new QAction(tr("About &Qt"), this); 74 | aboutQtAction->setStatusTip(tr("Show the Qt library's About box")); 75 | connect(aboutQtAction, &QAction::triggered, qApp, QApplication::aboutQt); 76 | } 77 | 78 | void MainWindow::createMenus() 79 | { 80 | QMenu *options = menuBar()->addMenu(tr("&Options")); 81 | options->addAction(newGameAction); 82 | options->addSeparator(); 83 | options->addAction(pauseAction); 84 | options->addAction(resumeAction); 85 | options->addSeparator(); 86 | options->addAction(exitAction); 87 | 88 | QMenu *help = menuBar()->addMenu(tr("&Help")); 89 | help->addAction(gameHelpAction); 90 | help->addAction(aboutAction); 91 | help->addAction(aboutQtAction); 92 | } 93 | 94 | void MainWindow::initScene() 95 | { 96 | scene->setSceneRect(-100, -100, 200, 200); 97 | } 98 | 99 | void MainWindow::initSceneBackground() 100 | { 101 | QPixmap bg(TILE_SIZE, TILE_SIZE); 102 | QPainter p(&bg); 103 | p.setBrush(QBrush(Qt::gray)); 104 | p.drawRect(0, 0, TILE_SIZE, TILE_SIZE); 105 | 106 | view->setBackgroundBrush(QBrush(bg)); 107 | } 108 | 109 | void MainWindow::newGame() 110 | { 111 | QTimer::singleShot(0, game, SLOT(gameOver())); 112 | } 113 | 114 | void MainWindow::about() 115 | { 116 | QMessageBox::about(this, tr("About this Game"), tr("

Snake Game

" 117 | "

Copyright © XXX." 118 | "

This game is a small Qt application. It is based on the demo in the GitHub written by Devbean.")); 119 | } 120 | 121 | void MainWindow::gameHelp() 122 | { 123 | QMessageBox::about(this, tr("Game Help"), tr("Using direction keys to control the snake to eat the food" 124 | "

Space - pause & resume")); 125 | } 126 | -------------------------------------------------------------------------------- /mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | 6 | class QGraphicsScene; 7 | class QGraphicsView; 8 | 9 | class GameController; 10 | 11 | class MainWindow : public QMainWindow 12 | { 13 | Q_OBJECT 14 | public: 15 | MainWindow(QWidget *parent = 0); 16 | ~MainWindow(); 17 | 18 | private slots: 19 | void adjustViewSize(); 20 | void newGame(); 21 | void gameHelp(); 22 | void about(); 23 | 24 | private: 25 | void createActions(); 26 | void createMenus(); 27 | 28 | void initScene(); 29 | void initSceneBackground(); 30 | 31 | 32 | QGraphicsScene *scene; 33 | QGraphicsView *view; 34 | 35 | GameController *game; 36 | 37 | QAction *newGameAction; 38 | QAction *pauseAction; 39 | QAction *resumeAction; 40 | QAction *exitAction; 41 | QAction *gameHelpAction; 42 | QAction *aboutAction; 43 | QAction *aboutQtAction; 44 | }; 45 | 46 | #endif // MAINWINDOW_H 47 | -------------------------------------------------------------------------------- /myapp.rc: -------------------------------------------------------------------------------- 1 | IDI_ICON1 ICON DISCARDABLE "snake.ico" -------------------------------------------------------------------------------- /res.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | snake.ico 4 | 5 | 6 | -------------------------------------------------------------------------------- /snake.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "constants.h" 4 | #include "gamecontroller.h" 5 | #include "snake.h" 6 | 7 | static const qreal SNAKE_SIZE = TILE_SIZE; 8 | 9 | Snake::Snake(GameController &controller) : 10 | head(0, 0), 11 | growing(7), 12 | speed(5), 13 | moveDirection(NoMove), 14 | controller(controller) 15 | { 16 | } 17 | 18 | QRectF Snake::boundingRect() const 19 | { 20 | qreal minX = head.x(); 21 | qreal minY = head.y(); 22 | qreal maxX = head.x(); 23 | qreal maxY = head.y(); 24 | 25 | foreach (QPointF p, tail) { 26 | maxX = p.x() > maxX ? p.x() : maxX; 27 | maxY = p.y() > maxY ? p.y() : maxY; 28 | minX = p.x() < minX ? p.x() : minX; 29 | minY = p.y() < minY ? p.y() : minY; 30 | } 31 | 32 | QPointF tl = mapFromScene(QPointF(minX, minY)); 33 | QPointF br = mapFromScene(QPointF(maxX, maxY)); 34 | 35 | QRectF bound = QRectF(tl.x(), // x 36 | tl.y(), // y 37 | br.x() - tl.x() + SNAKE_SIZE, // width 38 | br.y() - tl.y() + SNAKE_SIZE //height 39 | ); 40 | return bound; 41 | } 42 | 43 | QPainterPath Snake::shape() const 44 | { 45 | QPainterPath path; 46 | path.setFillRule(Qt::WindingFill); 47 | 48 | path.addRect(QRectF(0, 0, SNAKE_SIZE, SNAKE_SIZE)); 49 | 50 | foreach (QPointF p, tail) { 51 | QPointF itemp = mapFromScene(p); 52 | path.addRect(QRectF(itemp.x(), itemp.y(), SNAKE_SIZE, SNAKE_SIZE)); 53 | } 54 | 55 | return path; 56 | } 57 | 58 | void Snake::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) 59 | { 60 | painter->save(); 61 | painter->fillPath(shape(), Qt::yellow); 62 | painter->restore(); 63 | } 64 | 65 | void Snake::setMoveDirection(Direction direction) 66 | { 67 | if (moveDirection == MoveLeft && direction == MoveRight) 68 | return; 69 | if (moveDirection == MoveRight && direction == MoveLeft) 70 | return; 71 | if (moveDirection == MoveUp && direction == MoveDown) 72 | return; 73 | if (moveDirection == MoveDown && direction == MoveUp) 74 | return; 75 | moveDirection = direction; 76 | } 77 | 78 | Snake::Direction Snake::currentDirection() 79 | { 80 | return moveDirection; 81 | } 82 | 83 | void Snake::advance(int step) 84 | { 85 | if (!step) { 86 | return; 87 | } 88 | if (tickCounter++ % speed != 0) { 89 | return; 90 | } 91 | if (moveDirection == NoMove) { 92 | return; 93 | } 94 | 95 | if (growing > 0) { 96 | QPointF tailPoint = head; 97 | tail << tailPoint; 98 | growing -= 1; 99 | } else { 100 | tail.removeFirst(); 101 | tail << head; 102 | } 103 | 104 | switch (moveDirection) { 105 | case MoveLeft: 106 | moveLeft(); 107 | break; 108 | case MoveRight: 109 | moveRight(); 110 | break; 111 | case MoveUp: 112 | moveUp(); 113 | break; 114 | case MoveDown: 115 | moveDown(); 116 | break; 117 | } 118 | 119 | setPos(head); 120 | handleCollisions(); 121 | } 122 | 123 | void Snake::moveLeft() 124 | { 125 | head.rx() -= SNAKE_SIZE; 126 | if (head.rx() < -100) { 127 | head.rx() = 90; 128 | } 129 | } 130 | 131 | void Snake::moveRight() 132 | { 133 | head.rx() += SNAKE_SIZE; 134 | if (head.rx() >= 100) { 135 | head.rx() = -100; 136 | } 137 | } 138 | 139 | void Snake::moveUp() 140 | { 141 | head.ry() -= SNAKE_SIZE; 142 | if (head.ry() < -100) { 143 | head.ry() = 90; 144 | } 145 | } 146 | 147 | void Snake::moveDown() 148 | { 149 | head.ry() += SNAKE_SIZE; 150 | if (head.ry() >= 100) { 151 | head.ry() = -100; 152 | } 153 | } 154 | 155 | void Snake::handleCollisions() 156 | { 157 | QList collisions = collidingItems(); 158 | 159 | // Check collisions with other objects on screen 160 | foreach (QGraphicsItem *collidingItem, collisions) { 161 | if (collidingItem->data(GD_Type) == GO_Food) { 162 | // Let GameController handle the event by putting another apple 163 | controller.snakeAteFood((Food *)collidingItem); 164 | growing += 1; 165 | } 166 | } 167 | 168 | // Check snake eating itself 169 | if (tail.contains(head)) { 170 | controller.snakeAteItself(); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /snake.h: -------------------------------------------------------------------------------- 1 | #ifndef SNAKE_H 2 | #define SNAKE_H 3 | 4 | #include 5 | #include 6 | 7 | class GameController; 8 | 9 | class Snake : public QGraphicsItem 10 | { 11 | public: 12 | enum Direction { 13 | NoMove, 14 | MoveLeft, 15 | MoveRight, 16 | MoveUp, 17 | MoveDown 18 | }; 19 | 20 | Snake(GameController & controller); 21 | 22 | QRectF boundingRect() const; 23 | QPainterPath shape() const; 24 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *); 25 | 26 | void setMoveDirection(Direction direction); 27 | Direction currentDirection(); 28 | 29 | protected: 30 | void advance(int step); 31 | 32 | private: 33 | void moveLeft(); 34 | void moveRight(); 35 | void moveUp(); 36 | void moveDown(); 37 | 38 | void handleCollisions(); 39 | 40 | QPointF head; 41 | int growing; 42 | int speed; 43 | QList tail; 44 | int tickCounter; 45 | Direction moveDirection; 46 | GameController &controller; 47 | }; 48 | 49 | #endif // SNAKE_H 50 | -------------------------------------------------------------------------------- /snake.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devbean/snake-game/dfad697c3888df67aea53a58ea6692e280edadcb/snake.ico -------------------------------------------------------------------------------- /snake.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2012-12-11T19:56:10 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = snake 12 | TEMPLATE = app 13 | 14 | 15 | SOURCES += main.cpp\ 16 | mainwindow.cpp \ 17 | food.cpp \ 18 | gamecontroller.cpp \ 19 | snake.cpp \ 20 | wall.cpp 21 | 22 | HEADERS += mainwindow.h \ 23 | food.h \ 24 | gamecontroller.h \ 25 | constants.h \ 26 | snake.h \ 27 | wall.h 28 | 29 | RESOURCES += \ 30 | res.qrc 31 | 32 | -------------------------------------------------------------------------------- /wall.cpp: -------------------------------------------------------------------------------- 1 | #include "wall.h" 2 | 3 | Wall::Wall() 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /wall.h: -------------------------------------------------------------------------------- 1 | #ifndef WALL_H 2 | #define WALL_H 3 | 4 | #include 5 | 6 | class Wall : public QGraphicsItem 7 | { 8 | public: 9 | Wall(); 10 | }; 11 | 12 | #endif // WALL_H 13 | --------------------------------------------------------------------------------