beginning a snake SFML

Started by
4 comments, last by Satharis 10 years, 7 months ago

Hello. I'm trying to make a snake in C++ with SFML.

The code is fairly small, what I did atm is that when we press one key (left/right/up/down) the direction change, and while the window is open there are 4 while loops: while(direction = right/left/down/up) (one loop for each direction) and in that loop I put sprite.move(x,y).

I really can't find the error. It's maybe a C++ error but I really can't find it.

Here is the code :


#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <vector>


sf::Event event;
enum Direction { Up, Down, Left, Right};
int dir = Up;

class SnakeBlock
{
    public:

    SnakeBlock * next;
    sf::Texture texture;
    sf::Sprite snakeblock;
};

int main()
{
    sf::Sprite background;
    sf::Texture backgroundtex;
    backgroundtex.loadFromFile("background.png", sf::IntRect(0, 0, 1987, 1315));
    background.setTexture(backgroundtex);

    SnakeBlock snakeHead;
    snakeHead.texture.loadFromFile("spritesheetsnake.png", sf::IntRect(0,0,52,44));
    snakeHead.snakeblock.setTexture(snakeHead.texture);

    std::vector<SnakeBlock> Snake;
    Snake.push_back(snakeHead);
    Snake[0].snakeblock.setPosition(100,100);

    sf::RenderWindow window(sf::VideoMode(800,600), "SFML Snake");
    window.setFramerateLimit(30);
    while(window.isOpen())
    {
        while(window.pollEvent(event))
        {
            switch(event.type)
            {
                case sf::Event::Closed:

                window.close();
                break;

                default:
                break;
            }

        }

        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
        {
                dir = Left;
        }
        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
        {
                dir = Right;
        }
        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
        {
                dir = Down;
        }
        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
        {
                dir = Up;
        }
        while(dir == Up)
        {
            Snake[0].snakeblock.move(0,-5);
        }
        while(dir == Down)
        {
            Snake[0].snakeblock.move(0,5);
        }
        while(dir == Left)
        {
            Snake[0].snakeblock.move(-5,0);
        }
        while(dir == Right)
        {
            Snake[0].snakeblock.move(5,0);
        }
        window.clear(sf::Color::Green);

        window.draw(background);
        window.draw(Snake[0].snakeblock);

        window.display();
    }
    return 0;

}

By the way, if that code works, after that, I plan to make the snake bigger when he collide with something, delete the thing he went into, and add one snakeblock to the vector of snakeblock called Snake, then I will get (the last element minus one) of Snake and initialize the *next variable pointer to the last element, and in the main isOpen loop, I will make a for loop where I draw the last block of the snake, and then draw each *next of the snakeblock, but I wonder how I could make the snake not look like a line of blocks but such as that :

_______

|.............|___O

|

where the line is the body of the snake and the "O" is the head of the snake.

If you don't understand what I said about making the snake game just try to find the error in my code, I know what I said is not very clear.

Thanks you a lot in advance.

Advertisement

From a short look at the code those 4 "while" are likely endless loops which stop your program, just change them to "if". You already have the outer loops to make the game go on.

Also next time please write what the symptoms of the problem are, i.e. the program hangs and stops responding/I got this or that error message/whatever.

Better yet, don't even have a "dir" variable; rather, when you capture the direction the user presses from checking the keyboard state, move the snake in that direction. Also, I'd suggest using else if, otherwise, if the user is pressing 2 directions at once, the snake will both both directions.

Like this:

if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) {
  Snake.move(-5, 0)
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) {
  Snake.move(5, 0)
}
...

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

Thanks for the tip about the loops, replaced with if and now I can move snakehead.

No you didn't get the point BeerNuts, I'm trying to make a snake, a snake always move in the direction you gave him, even if you stopped pressing the button !

That is why I'm using a direction enum, also I think I could need it for making the rest of the snake follow the head, such as the snake could potentially "bite" itself.

I've been doing lots of upgrades. here is what I got now :


#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <SFML/Audio.hpp>
#include <deque>

void selfIncrement();

sf::Event event;
sf::Clock clockSnake;
sf::Time elapse;

enum Direction { Up, Down, Left, Right};

int dir = Up;

int n = 1;

class SnakeBlock
{
    public:

    SnakeBlock * next;
    sf::Texture texture;
    sf::Sprite snakeblock;
    int lastX, lastY;
};

std::deque<SnakeBlock> Snake;

int main()
{
    elapse = clockSnake.getElapsedTime();

    sf::Music epicMusic;
    epicMusic.openFromFile("epicmusic.wav");
    epicMusic.play();

    SnakeBlock snakeHead;
    snakeHead.texture.loadFromFile("spritesheetsnake.png", sf::IntRect(0,0,25,22));
    snakeHead.snakeblock.setTexture(snakeHead.texture);
    SnakeBlock snakeBody1;
    snakeBody1.snakeblock.setTexture(*(snakeHead.snakeblock.getTexture()));
    SnakeBlock snakeBody2;
    snakeBody2.snakeblock.setTexture(*(snakeHead.snakeblock.getTexture()));

    Snake.push_front(snakeHead);
    Snake.push_front(snakeBody1);
    Snake.push_front(snakeBody2);
    Snake[2].snakeblock.setPosition(500,350);
    Snake[1].snakeblock.setPosition(475, 338);
    Snake[0].snakeblock.setPosition(450, 316);

    sf::RenderWindow window(sf::VideoMode(1028,768), "SFML Snake");
    window.setFramerateLimit(30);
    while(window.isOpen())
    {
        while(window.pollEvent(event))
        {
            switch(event.type)
            {
                case sf::Event::Closed:
                epicMusic.stop();
                window.close();
                break;

                default:
                break;
            }

        }

        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
        {
                dir = Left;
        }
        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
        {
                dir = Right;
        }
        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
        {
                dir = Down;
        }
        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
        {
                dir = Up;
        }
        if(dir == Up)
        {

            Snake[0].snakeblock.move(0,-5);
            Snake[n].snakeblock.setPosition(Snake[n-1].snakeblock.getPosition().x, Snake[n-1].snakeblock.getPosition().y-22);
        }
        if(dir == Down)
        {
            Snake[0].snakeblock.move(0,5);
            Snake[n].snakeblock.setPosition(Snake[n-1].snakeblock.getPosition().x, Snake[n-1].snakeblock.getPosition().y+22);
        }
        if(dir == Left)
        {
            Snake[0].snakeblock.move(-5,0);
            Snake[n].snakeblock.setPosition(Snake[n-1].snakeblock.getPosition().x+25, Snake[n-1].snakeblock.getPosition().y);
        }
        if(dir == Right)
        {
            Snake[0].snakeblock.move(5,0);
            Snake[n].snakeblock.setPosition(Snake[n-1].snakeblock.getPosition().x-25, Snake[n-1].snakeblock.getPosition().y);
        }
        window.clear(sf::Color::Green);
        selfIncrement();
        for(unsigned int m = 0; m < Snake.size(); m++)
        {
            window.draw(Snake[m].snakeblock);
        }

        window.display();
    }
    return 0;

}

void selfIncrement()
{
    if(elapse.asSeconds() > 3)
    {
        n++;
        clockSnake.restart();
    }
    if(n > Snake.size())
    {
        n = 1;
    }
}


I got one problem:

I don't know how to do what I said, aka "make the snake movement to make it possible to bite himself". What I got now : each snakeblock will take the last snakeblock position, resulting in a perfectly straight snake. :/

Maybe I need to use one variable that auto increments over time, resulting to update the position of each snakeblock one by one, or maybe I need to use the enumeration Direction, or maybe I need to do both of that, but I don't really know how to practically do so.

Some ideas would greatly help me ! Thanks.

I also thought it would be complicated to make the snake move like in the original snake game, but i came up with this easy idea:


for (unsigned int i = snake.size()-1; i > 0; i--) // start with the last element(body part)
{
	//snake[i] = snake[i-1]; // works for my code
//you would have to use       snake[i].lastX = snake[i-1].lastX;   and     snake[i].lastY = snake[i-1].lastY;
}

Its important that your "snake-head" is the 0th element of the snake vector. I have just a vector<vec2> snake (vec2 is just x and y). So every time you move your snake-head in any direction the x and y of the next body-block just becomes the old head position(or the position of the block in the vector before it).

Here's how I did it:


    void Python::MoveBody()
    {
        // Move the head based on direction.
        switch(m_direction)
        {
        case DIRECTION_UP:
            m_tilePosition.y -= 1;
            break;
        case DIRECTION_RIGHT:
            m_tilePosition.x += 1;
            break;
        case DIRECTION_DOWN:
            m_tilePosition.y += 1;
            break;
        case DIRECTION_LEFT:
            m_tilePosition.x -= 1;
            break;
        }

        // Move the body segments by telling each segment the direction of the segment before it.
        for(std::vector<PythonSegment>::size_type i = 0; i < m_segmentStack.size(); i++)
        {
            // Pass the head's direction if the segment is the first body piece, otherwise pass the previous segment.
            if(i == 0)
            {
                m_segmentStack[i].SetDirection(m_previousMoveDirection);
            }
            else
            {
                m_segmentStack[i].SetDirection(m_segmentStack[i - 1].GetPreviousMoveDirection());
            }

            m_segmentStack[i].Move();
        }

        for(std::vector<PythonSegment>::size_type i = 0; i < m_segmentStack.size(); i++)
        {
            m_segmentStack[i].FinishMove();
        }

        m_previousMoveDirection = m_direction;
    }

Essentially the snake head is the object the player controls, it has a direction that is modified based on player input and after a period of time it will step to the tile it is facing. When it steps it basically loops through and passes the previous facing direction of each segment to the one following it. Essentially if the head goes up then you want the second piece to go left and then up.

A little messy, FinishMove() basically sets the previous direction to the current direction after all the pieces move, but it does work. It also works for an arbitrary number of snake pieces.

In terms of growing the body this system helps a lot too, all you have to do when you collide with food is add to some "grow count" variable, then whenever the snake takes a step, if the growcount is greater than zero you decrement it by one and push a new snake piece onto the snake where the last piece just was.

This topic is closed to new replies.

Advertisement