Qt Snake game model/view/controller

Hello everyone. I have a snake game in C++ Qt. To get a working game I have done it in one file. Now I need to split it into model/view/controller.
Model is game logic, view is gui stuff and controller is the 'glue'
Its proving to be very difficult, constantly getting member access into incomplete type controller.

I am still trying to learn, but I can't see how I can fix it?

Any help is greatly appreciated.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
  #ifndef MODEL_H
#define MODEL_H

#include "controller.h"
#include "coordinate.h"
#include <vector>

class controller;

enum class Turn{ left, right, up, down };
enum class Movement{ north, south, east, west };

class model
{
private:
    controller *c;

    // store obstacles and check for duplicity
    std::vector<coordinate> placed_obstacles;
    //vevtor for snake coordinates
    std::vector<coordinate> the_snake;

    //this variable is needed otherwise snake is drawn on top of itself
    const int BODY_SIZE = 10;
    int timerId;
    int snake_size = 3;//initial size of the snake
    coordinate apple;
    coordinate snake;

    //delay offset for the speed game/refresh
    const int DELAY = 100;
    int score = 0;


    Turn change;
    Movement direction;
public:
    model();
    model(controller* c) : c(c) { }
    void initGame();

    void placeFood();
    void placeObstacle();
};


inline void model::initGame() {

    for(int i = 0; i < snake_size; i++) {
            snake.m_x = 150 - (i * BODY_SIZE);
            snake.m_y = 150;
            the_snake.push_back(snake);
        }

    placeFood();
    placeObstacle();
    timerId = c->Ctimer(DELAY);
}


#endif // MODEL_H

#ifndef VIEW_H
#define VIEW_H

#include <QPainter>
#include <QTime>
#include <QIcon>
//#include <QtMultimedia>
//#include <QtMultimediaWidgets>
//#include <QSound>
#include "snake.h"
#include <iostream>
#include <random>

#include <QWidget>
#include <QKeyEvent>
#include <vector>
#include "controller.h"
//#include "coordinate.h"

//class controller;


class view : public QWidget
{
private:

    controller *c;

    QImage dot_png;
    QImage head_png;
    QImage apple_png;
    QImage obstacle_png;
    //play area width
    static const int B_WIDTH = 500;
    //play area height
    static const int B_HEIGHT = 500;
    //delay offset for the speed game/refresh
    //const int DELAY = 100;

    //this variable is needed otherwise snake is drawn on top of itself
    //const int BODY_SIZE = 10;

public:
    view(QWidget *parent = 0);
    view(controller * c) : c(c) { }
    void loadImages();
    void initGame();

    int Timer(int DELAY);

};


//constructor
view::view(QWidget *parent) : QWidget(parent) {

    setStyleSheet("background-color:grey;");

    //set the paly area size
    resize(B_WIDTH, B_HEIGHT);
    //load the png files for the game
    loadImages();

    //init game
    initGame();

}

inline void view::loadImages() {

    dot_png.load(":/new/prefix1/dot.png");
    head_png.load(":/new/prefix1/head.png");
    apple_png.load(":/new/prefix1/apple.png");
    obstacle_png.load(":/new/prefix1/obstacle.png");
}

inline int view::Timer(int DELAY) {
    return startTimer(DELAY);
}

inline void view::initGame() {
    c->CinitGame();
}


#endif // VIEW_H


#ifndef CONTROLLER_H
#define CONTROLLER_H

#include "model.h"
#include "view.h"


class controller
{
private:
    model* m;
    view* v;
public:
    controller() : m(new model(this)), v(new view(this)) {
        v->show();
    }


    void CinitGame() {
        m->initGame();
    }

    int Ctimer(int DELAY) {
        return v->Timer(DELAY);
    }
};

#endif // CONTROLLER_H

#ifndef COORDINATE_H
#define COORDINATE_H

class coordinate
{
 public:
    int m_x;
    int m_y;

    coordinate(int x=0, int y=0) : m_x(x), m_y(y) {}

    //copy constructor
    coordinate(const coordinate &other) {
        m_x = other.m_x;
        m_y = other.m_y;
    }

    //assignment overload operator
    coordinate& operator=(const coordinate &coord) {
        m_x = coord.m_x;
        m_y = coord.m_y;
        return *this;
    }

    bool operator==(const coordinate& rhs) {
        return this->m_x == rhs.m_x && this->m_y == rhs.m_y;
    }


};

#endif // COORDINATE_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
#include "snake.h"

#include <QPainter>
#include <QTime>
#include <QIcon>
#include <QtMultimedia>
#include <QtMultimediaWidgets>
#include <QSound>
#include "snake.h"
#include <iostream>
#include <random>
#include <QGamepad>
#include <QGamepadKeyNavigation>
#include <QGamepadManager>


//constructor
Snake::Snake(QWidget *parent) : QWidget(parent) {

    setStyleSheet("background-color:grey;");
    //start snake moving in west direction
    change = Turn::right;
    direction = Movement::west;
    GamePlay = true;

    //set the paly area size
    resize(B_WIDTH, B_HEIGHT);
    //load the png files for the game
    loadImages();

    //init game
    initGame();
}

void Snake::loadImages() {

    dot_png.load(":/new/prefix1/dot.png");
    head_png.load(":/new/prefix1/head.png");
    apple_png.load(":/new/prefix1/apple.png");
    obstacle_png.load(":/new/prefix1/obstacle.png");
}

void Snake::initGame() {

    for(int i = 0; i < snake_size; i++) {
            snake.m_x = 150 - (i * BODY_SIZE);
            snake.m_y = 150;
            the_snake.push_back(snake);
        }

    placeFood();
    placeObstacle();
    timerId = startTimer(DELAY);
}

void Snake::paintEvent(QPaintEvent *e) {

    Q_UNUSED(e);

    doDrawing();
}

void Snake::doDrawing() {

    QPainter qp(this);

    std::cout << "number of obstacles->" << placed_obstacles.size() << std::endl;

    if(GamePlay) {
         //draw the obstacles
         for(unsigned long int i{}; i < placed_obstacles.size() ; i++) {
            qp.drawImage(placed_obstacles[i].m_x, placed_obstacles[i].m_y, obstacle_png);
         }
         //draw the apple
         qp.drawImage(apple.m_x, apple.m_y, apple_png);
         //draw the snake
         for (unsigned long int i = 0; i < the_snake.size(); i++) {
            if (i == 0) {
                qp.drawImage(the_snake[i].m_x, the_snake[i].m_y, head_png);
            }
            else {
                qp.drawImage(the_snake[i].m_x, the_snake[i].m_y, dot_png);
            }
        }

    }
    else {
        gameOver(qp);
    }
}

void Snake::gameOver(QPainter &qp) {

    QString GameOver = "Game over";
    QString YourScore = "Your score ";
    QString display_score = QString::number(score);
    QSound::play(":/new/prefix1/fail-trombone-03.wav");
    QFont font("Courier", 15, QFont::DemiBold);
    QFontMetrics fm(font);
    int textWidth = fm.width(GameOver);

    qp.setFont(font);
    int h = height();
    int w = width();

    qp.translate(QPoint(w/2, h/2));
    qp.drawText(-textWidth/2, 0, GameOver);
    qp.drawText(-textWidth/2, 20, YourScore+=display_score);
}

void Snake::grow(int amount) {
       coordinate last = the_snake.back();
       for(int i{}; i < amount; i++) {
           the_snake.push_back(last);
       }
   }

void Snake::foodCollision() {

    if((the_snake[0].m_x > apple.m_x - 15 && the_snake[0].m_x < apple.m_x + 15)
            && (the_snake[0].m_y > apple.m_y - 15 && the_snake[0].m_y < apple.m_y + 15)) {
        grow(3);
        score++;
        QSound::play(":/new/prefix1/button-1.wav");
        placeFood();
        if(the_snake.size() % 2) placeObstacle();

    }
}

void Snake::obstacleCollision() {

    for(unsigned long int i{}; i < placed_obstacles.size() ; i++) {
        if ((the_snake[0].m_x > placed_obstacles[i].m_x - 10 && the_snake[0].m_x < placed_obstacles[i].m_x + 10)
            && (the_snake[0].m_y > placed_obstacles[i].m_y - 10 && the_snake[0].m_y < placed_obstacles[i].m_y + 10)) {
            QSound::play(":/new/prefix1/button-1.wav");
            GamePlay = false;
            break;
        }
            if(!GamePlay) {
                killTimer(timerId);
            }
        }
}



void Snake::move() {

    for (int i = the_snake.size()-1; i > 0; i--) {
        the_snake[i] = the_snake[i - 1];
    }

    switch(direction){
            case Movement::north:
                the_snake[0].m_y-=BODY_SIZE;
                break;
            case Movement::south:
                the_snake[0].m_y+=BODY_SIZE;
                break;
            case Movement::east:
                the_snake[0].m_x-=BODY_SIZE;
                break;
            case Movement::west:
                the_snake[0].m_x+=BODY_SIZE;
            break;

            }
}

void Snake::checkCollision() {

    //below for self collision
    for(unsigned long int i{1}; i < the_snake.size() - 1; i++) {
                if(the_snake[0] == the_snake[i]) { GamePlay = false; break; }
            }

    if(!GamePlay) {
        killTimer(timerId);
    }


    //add GamePlay = false to each if() to disable wraparound
    //and enable wall crash
    if(the_snake[0].m_y >= B_HEIGHT){
        the_snake[0].m_y = 0;
    }

    if(the_snake[0].m_y < 0){
        the_snake[0].m_y = B_HEIGHT;
    }

    if(the_snake[0].m_x >= B_WIDTH){
        the_snake[0].m_x = 0;
    }

    if(the_snake[0].m_x < 0){
        the_snake[0].m_x = B_WIDTH;
    }

}

void Snake::placeFood() {

    bool notundersnake{true};
    std::random_device dev;
    std::uniform_int_distribution<int> dist(0,B_HEIGHT-BODY_SIZE);
    coordinate temp((dist(dev)), (dist(dev)));
    //check to make sure apple isnt placed under snake
    for(unsigned long int i{}; i < the_snake.size(); i++ ) {
        if((temp.m_x > the_snake[i].m_x - 15 && temp.m_x < the_snake[i].m_x + 15)
            && (temp.m_y > the_snake[i].m_y - 15 && temp.m_y < the_snake[i].m_y +15))
        { notundersnake = false; break; }
    }
    if(notundersnake) apple = temp;
}


void Snake::placeObstacle() {

    bool not_duplicate{true};
    std::random_device dev;
    std::uniform_int_distribution<int> dist(0,B_HEIGHT-BODY_SIZE);
    coordinate p((dist(dev)), (dist(dev)));
    std::cout << p.m_x << " " << p.m_y << std::endl;
    if(placed_obstacles.empty()) placed_obstacles.push_back(p);
    else {
        for(unsigned long int i{}; i < placed_obstacles.size() -1; i++) {
            if(p == placed_obstacles[i]) { not_duplicate = false; break; }
            if((placed_obstacles[i].m_x > apple.m_x - 100 && placed_obstacles[i].m_x < apple.m_x + 100)
                && (placed_obstacles[i].m_y > apple.m_y - 100 && placed_obstacles[i].m_y < apple.m_y + 100)) {
                not_duplicate = false; break;
            }
        }
        if(not_duplicate) { placed_obstacles.push_back(p); }
    }
}


void Snake::timerEvent(QTimerEvent *e) {

    Q_UNUSED(e);

    if (GamePlay) {

        foodCollision();
        obstacleCollision();
        checkCollision();
        move();
    }

    repaint();
}

void Snake::keyPressEvent(QKeyEvent *e) {

    int key = e->key();


    if ((key == Qt::Key_Left) && (!(direction == Movement::west))) {
        direction = Movement::east;
    }

    if ((key == Qt::Key_Right) && (!(direction == Movement::east))) {
        direction = Movement::west;
    }

    if ((key == Qt::Key_Up) && (!(direction == Movement::south))) {
        direction = Movement::north;
    }

    if ((key == Qt::Key_Down) && (!(direction == Movement::north))) {
        direction = Movement::south;
    }

    QWidget::keyPressEvent(e);
}
sorry forgot to add original game

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

#ifndef SNAKE_H
#define SNAKE_H


#include <QWidget>
#include <QKeyEvent>
#include <vector>
#include "coordinate.h"

enum class Turn{ left, right, up, down };
enum class Movement{ north, south, east, west };



class Snake : public QWidget {

  public:
      Snake(QWidget *parent = 0);
      // store obstacles and check for duplicity
      std::vector<coordinate> placed_obstacles;
      //vevtor for snake coordinates
      std::vector<coordinate> the_snake;

      Turn change;
      Movement direction;


      void paintEvent(QPaintEvent *);
      void timerEvent(QTimerEvent *);
      void keyPressEvent(QKeyEvent *);

      QImage dot_png;
      QImage head_png;
      QImage apple_png;
      QImage obstacle_png;

      //play area width
      static const int B_WIDTH = 500;
      //play area height
      static const int B_HEIGHT = 500;

      //this variable is needed otherwise snake is drawn on top of itself
      const int BODY_SIZE = 10;

      //delay offset for the speed game/refresh
      const int DELAY = 100;
      int score = 0;


      int timerId;
      int snake_size = 3;//initial size of the snake
      coordinate apple;
      coordinate snake;


      //check game state playing/gameover
      bool GamePlay;

      void grow(int amount);
      void loadImages();
      void initGame();
      void placeFood();
      void placeObstacle();
      void foodCollision();
      void obstacleCollision();
      void checkCollision();
      void move();
      void doDrawing();
      void gameOver(QPainter &);
};


#endif // SNAKE_H

Topic archived. No new replies allowed.