What's wrong with this snake game code?

Started by
32 comments, last by RobTheBloke 8 years, 1 month ago

Hello,

I've been trying to make a snake game, and I had lots of problems and debugging errors related to the vector, which I though I eventually solved, but now I discovered that the snake isn't moving at all when I use the keyboard keys , let alone increasing in length upon eating a fruit. Could you please check my code and help me by telling me what's wrong/what can I do to solve the problem/ways to improve my code? Thank you!

I'm a beginner, so please excuse me if the question is stupid/the solution is obvious. (I've done my research, believe me.).


#include <vector>
#include <limits>
#include <algorithm>




#include <SFML/Graphics.hpp>
#include <iostream>
int t = 0;
sf::RectangleShape addsnake();
sf::RectangleShape first;
sf::RectangleShape second;
sf::RectangleShape third;
sf::RectangleShape fourth;




std::vector<sf::RectangleShape>snake{first,second,third,fourth };




sf::RectangleShape addsnake(){


snake[0].setFillColor(sf::Color::Red);
snake[0].setPosition(100, 100);
snake[0].setSize(sf::Vector2f(20, 20));
return snake[t];


}
bool intersects(const sf::RectangleShape & r1, const sf::RectangleShape & r2){
sf::FloatRect snake = r1.getGlobalBounds();
sf::FloatRect spawnedFruit = r2.getGlobalBounds();
return snake.intersects(spawnedFruit);


}


sf::RectangleShape generateFruit() {




sf::RectangleShape fruit;
fruit.setFillColor(sf::Color::Yellow);
int fruitx = rand() % 400;
int fruity = rand() % 400;
fruit.setPosition(fruitx, fruity);
fruit.setSize(sf::Vector2f(5, 5));


return fruit;




}
int main()
{
srand(time(NULL));
int width = 400;
int height = 400;
sf::VideoMode videomode(width, height);
sf::RenderWindow window(videomode, "Snake");










sf::Clock clock;
sf::Time t1 = sf::seconds(20);
sf::RectangleShape spawnedFruit;
while (window.isOpen()) {
window.clear();
window.draw(snake[0]);






sf::Time elapsed1 = clock.getElapsedTime();
if (elapsed1 >= t1) {




spawnedFruit = generateFruit();


clock.restart();


}


window.draw(spawnedFruit);


window.display();
sf::Event event;
while (window.pollEvent(event))
{
if ((event.type == sf::Event::Closed) ||
((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape)))
window.close();
}




if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))


snake[0].move(0, -0.1);
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
snake[0].move(0, 0.1);
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
snake[0].move(-0.1, 0);
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
snake[0].move(0.1, 0);


if (intersects(snake[0], spawnedFruit))


t++;


snake.push_back(addsnake()); //tried snake[t]


















}


}




Dream in the shadows

Advertisement
With a small formatting change, the problem becomes obvious:
if (intersects(snake[0], spawnedFruit))
    t++;
snake.push_back(addsnake()); //tried snake[t]
That push_back will be called every frame. If you only want the snake to grow when you intersect a fruit, put braces around both the increment of t and the push_back call.

sf::RectangleShape addsnake(){
    snake[0].setFillColor(sf::Color::Red);
    snake[0].setPosition(100, 100);
    snake[0].setSize(sf::Vector2f(20, 20));
    return snake[t];
}
This function will position the first rectangle of the snake in the same place every time addsnake() is called - which is every frame, given my above point. Did you mean those 0s to be 't's? In any case, this won't work as you intend, because snake[t] has to already exist for that to work. You need to either change this function to create a rectangle shape and return it or change it to call push_back (or emplace_back) before trying to reference snake[t].

But isn't snake[t] the t-th element in my vector which is a rectangle shape?

Also, after I added the braces around both t++ and the push back, the snake doesn't even show, I'm really confused.

Dream in the shadows

But isn't snake[t] the t-th element in my vector which is a rectangle shape?


Hmmm, at a second glance, I may be misunderstanding how you're actually trying to implement this. I see now that snake[t] does always exist because because you've set it up so that snake.size() is always greater than t, but I'm not sure I understand why you chose to do it this way. It may help if you explain what you're trying to do, in pseudocode or English. The forum breaking the formatting of your code doesn't exactly help matters.

Here is what appears to be happening with the code you posted:

0. t starts at 0. At the start, it appears that you've initialized it with 4 rectangle shapes, so snake.size() would be 4 (a fact that I missed, initially).
1. You call push_back(addsnake()).
3. Now you're in addsnake. addsnake repositions snake[0] and then returns snake[0].
4. push_back adds a new element to snake and copies snake[0] into it.
5. Now you loop, draw, etc.
6. Now you call push_back(addsnake()) again, and the same thing happens.
...

So all your code was doing was taking the first snake element, repositioning it, and adding it continuously to the end of the snake, making the snake grow and grow until you run out of memory.
If your code incremented t, you would be copying snake[t], which would never get modified because you only ever set the position of snake[0].


Also, after I added the braces around both t++ and the push back, the snake doesn't even show, I'm really confused.



The positions, sizes, and fill colours of the initial elements of snake are never set. The only reason anything was being drawn was because you were setting snake[0]'s fields in addsnake, and coincidentally only drawing snake[0].

I'm still very confused, what I'm trying to do is:

draw snake[0]

spawn fruit every 20 seconds (I know this is not how the real snake it, but whatever)

if the head of the snake(snake[0]) collides with the fruit, add 1 to t (which is first 0) and then add snake[t] to snake[0]

I also changed all the 0's in setFillColor,size,position to t's, but the problem still persists :/

But aren't the initial elements of the snake set in addsnake() ? Were should I set them? in main()?

Dream in the shadows

I removed the setting of snake[t] from addsnake() and put them in main(), I also put the t increment in addsnake instead and made it return snake[t], the snake is now moving but intersecting with the fruit does nothing

Dream in the shadows

I also tried changing snake.push_back(addsnake()) to snake[0].push_back(addsnake()), but it gives me an Error on push_back (class sf::RectangleShape has no member "push_back").

Dream in the shadows

If you've updated the code, maybe you might post it here so we can see what you do.

But aren't the initial elements of the snake set in addsnake()


Nope, you're currently setting them when you declare snake:
sf::RectangleShape first;
sf::RectangleShape second;
sf::RectangleShape third;
sf::RectangleShape fourth;
std::vector<sf::RectangleShape>snake{first,second,third,fourth };
This code will take these 4 RectangleShapes, each of which is initialized to the default value of a RectangleShape, and put them in the vector.
So, once this code has executed, snake will have 4 identical RectangleShapes in it.

and then add snake[t] to snake[0]


What, specifically, do you mean by this? Are you trying to add a new element to the end of the snake?
std::vector<sf::RectangleShape>::push_back takes as input an sf::RectangleShape and copies it to the end of the vector.
snake.push_back(snake[t]) will therefore take snake[t] (an element that already exists in the vector!) and copy it to the end of snake.

So, to illustrate this a little more visually, before the push back snake looks like this:
{first,second,third,fourth }

If t = 0 and you call push_back(snake[t]) then it will look like this:
{first,second,third,fourth, first }

also tried changing snake.push_back(addsnake()) to snake[0].push_back(addsnake()), but it gives me an Error on push_back (class sf::RectangleShape has no member "push_back").


Okay, it seems like there's a few things that you're misunderstanding.

snake is a vector.
snake[0] is an element of the vector.
snake[t] and snake[0] are both elements of snake; adding one to the other is meaningless.
You can't call snake[0].push_back because snake[0] is not a vector - it's an sf::RectangleShape.

Remember that a vector is just a collection of things. A list. The individual things in a vector don't know that they're in a vector; they don't even know that the vector exists. They only know about themselves.

I understand what you mean, what I'm trying to do is make the next element of the existing vector appear after the first. I just want to increase the length of the snake upon colliding with the fruit, should I use something other than vectors better? Here is the updated code:-


#include <vector>
#include <limits>
#include <algorithm>




#include <SFML/Graphics.hpp>
#include <iostream>
int t = 0;
sf::RectangleShape addsnake();
sf::RectangleShape first;
sf::RectangleShape second;
sf::RectangleShape third;
sf::RectangleShape fourth;




std::vector<sf::RectangleShape>snake{first,second,third,fourth };




sf::RectangleShape addsnake(){


t++;




return snake[t];


}
bool intersects(const sf::RectangleShape & r1, const sf::RectangleShape & r2){
sf::FloatRect snake = r1.getGlobalBounds();
sf::FloatRect spawnedFruit = r2.getGlobalBounds();
return snake.intersects(spawnedFruit);


}


sf::RectangleShape generateFruit() {




sf::RectangleShape fruit;
fruit.setFillColor(sf::Color::Yellow);
int fruitx = rand() % 400;
int fruity = rand() % 400;
fruit.setPosition(fruitx, fruity);
fruit.setSize(sf::Vector2f(5, 5));


return fruit;




}
int main()
{
snake[t].setFillColor(sf::Color::Red);
snake[t].setPosition(100, 100);
snake[t].setSize(sf::Vector2f(20, 20));
srand(time(NULL));
int width = 400;
int height = 400;
sf::VideoMode videomode(width, height);
sf::RenderWindow window(videomode, "Snake");










sf::Clock clock;
sf::Time t1 = sf::seconds(20);
sf::RectangleShape spawnedFruit;
while (window.isOpen()) {
window.clear();
window.draw(snake[0]);






sf::Time elapsed1 = clock.getElapsedTime();
if (elapsed1 >= t1) {




spawnedFruit = generateFruit();


clock.restart();


}


window.draw(spawnedFruit);


window.display();
sf::Event event;
while (window.pollEvent(event))
{
if ((event.type == sf::Event::Closed) ||
((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape)))
window.close();
}




if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))


snake[0].move(0, -0.1);
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
snake[0].move(0, 0.1);
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
snake[0].move(-0.1, 0);
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
snake[0].move(0.1, 0);


if (intersects(snake[0], spawnedFruit)){






snake.push_back(addsnake());
}


//tried snake[t]


















}


}

I'm sorry if I look like a complete retard right now, which I think is true.

Dream in the shadows

Okay. That is what I thought, and why I was so confused about why you were adding snake[t]. If you hadn't already initialized snake with those 4 elements, that would be a meaningless operation. :)

I suggest that instead of initializing the vector with those four elements immediately (why are you doing this, by the way?), just put ONE element in the vector right before you start the game loop, and iterate from there. If you still need help after doing this, and carefully reading my explanations and considering their implications, post the current state of your code and we'll keep debugging.

This topic is closed to new replies.

Advertisement