Snake Game

Hi guys.

I am creating a snake game for a project and am massively stuck on something.

I'm trying to make the snake move in a 2D grid array. To explain this further, i'm using a game board space of 768x768 and want to implement a 20x20 grid for the snake and all other objects to be limited to.

I've currently got a snake head shape with user control but the way it is coded means that the shape moves 1 pixel at a time.

I'm completely at a loss as to what to do and would appreciate any help or suggestions.

This is the code for my snake movement.

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
void Snake::move()
{
    switch(direction_){
    case Direction::North:
        position_.y += 1;
        break;
    case Direction::East:
        position_.x += 1;
        break;
    case Direction::South:
        position_.y -= 1;
        break;
    case Direction::West:
        position_.x -= 1;
    }

    if (position_.x < 6.4) position_.x = 44.8; else if (position_.x > 44.8) position_.x = 6.4;
    if (position_.y < 0) position_.y = 38.4; else if (position_.y > 38.4) position_.y = 0;

}

void Snake::render(prg::Canvas& canvas) const
{

    canvas.drawCircle(getPosition().x * 20, getPosition().y * 20,19.2,prg::Colour::GREEN);
}

void Snake::changeDirection(Direction new_direction)
{
    direction_ = new_direction;
}



This is the code that controls what happens when a direction key is pressed

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
PlayerSnake::PlayerSnake()
{
    prg::application.addKeyListener(*this);
}

PlayerSnake::~PlayerSnake()
{
    prg::application.removeKeyListener(*this);
}

bool PlayerSnake::onKey(const prg::IKeyEvent::KeyEvent& key_event)
{
    if(key_event.key_state == KeyEvent::KB_DOWN) {
        switch(key_event.key) {

        case KeyEvent::KB_LEFT_KEY:
            changeDirection(Direction::West);
            break;
        case KeyEvent::KB_RIGHT_KEY:
            changeDirection(Direction::East);
            break;
        case KeyEvent::KB_UP_KEY:
            changeDirection(Direction::North);
            break;
        case KeyEvent::KB_DOWN_KEY:
            changeDirection(Direction::South);
            break;
        }
    }
    return true;
}



This is some code i have for the play state, not sure if it's relevant

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
bool PlayState::onCreate()
{
    snakes_.push_back(new AISnake);
    snakes_.back()->setPosition(Position(40,40));
    snakes_.push_back(new PlayerSnake);
    snakes_.back()->setPosition(Position(20,20));

return true;

}

bool PlayState::onDestroy()
{
    return true;
}

void PlayState::onEntry()
{
    prg::application.addKeyListener(*this);
    game_timer_.start();
}

void PlayState::onExit()
{
    prg::application.removeKeyListener(*this);
    game_timer_.stop();
}

void PlayState::onUpdate()
{

}

void PlayState::onRender(prg::Canvas& canvas)
{
    const std::string text = "";

    canvas.blitFast(
        background_,
        canvas.getWidth() / 2 - background_.getWidth() / 2,
        canvas.getHeight() / 2 - background_.getHeight() / 2
    );

    prg::uint text_dims[2];
    prg::Font::MASSIVE.computePrintDimensions(text_dims, text);
    prg::Font::MASSIVE.print(
      canvas,
      prg::application.getScreenWidth() / 2 - text_dims[0] / 2,
      prg::application.getScreenHeight() / 2 - text_dims[1] / 2,
      prg::Colour::RED,
      text);

    for(const auto snake : snakes_) {
    snake->render(canvas);
    }

    for(Shape shapes : shapes_) {
    shapes.render(canvas);
    }
}

bool PlayState::onKey(const prg::IKeyEvent::KeyEvent& key_event)
{
    if(key_event.key_state == KeyEvent::KB_DOWN) {
        switch(key_event.key) {
        case KeyEvent::KB_ESC_KEY:
            prg::application.exit();
            break;

        }
    }
    return true;
}

void PlayState::onTimer(prg::Timer& timer)
{
    for(auto snake : snakes_) {
        snake->move();
    }
}



Thanks for looking, hope someone can help me out in some way
Last edited on
That seems to be a really shitty way to do it.

Why not put coords (I would use std::pair of ints) into a queue, every time you move, you put a new one in and pop the last one. You would also want an int as a buffer when your snake extends, so it would check if you have anything in the buffer before popping the last one.
thanks for the reply, i'm aware it's probably a shitty way, i'm quite new to C++

i don't suppose you could suggest some code amendments or additions to implement what you suggested?

Thanks.
STL lists are a pretty basic concept, just look at some examples in this website's reference section.

Also, I realized that you are not looking for a queue, because you can't iterate through it, what you need is a deque, so you use push_front, and pop_back.

For pairs, you can totally just write a class called point or whatever and make it contain 2 ints called x, and y. But if you want to use std::pairs, it is in the header <utility>, but note that the <map> header also contains pairs.

So your snake trail list would look like this:
std::deque<std::pair<int,int>> snake_trail;

But pairs are not very pretty, because whenever you want to insert you have to do:
snake_trail.push_front(std::pair<int,int>(x,y));

And also to access the values, you have to call "first" and "second".

But I just like it because I don't have to make a class.
Thanks poteto, that's really helpful, i've had a go at making a snake trail list like you mentioned but have no idea where and how to put it. Could you point me to how it should look?
std::make_pair(x,y);
Topic archived. No new replies allowed.