0
\$\begingroup\$

I am currently trying to make a snake game with Java FX, I read several posts about it and I understand on theory how to move the snake body (for each part of the snake reference the previous one and move it to its former location) but I am facing some issues, my snake is not able to turn around properly:

Poor snake has issue to coordinate its movements

I would just like it to react as a normal snake, when the head steers into a direction then the other body parts follow one by one.

In my case I created a vector class to store positions and velocity (I use velocity to be able to move all the part at the same speed and therefore be able to keep the 3 pixel gap between them).

I usually trying my best to resolve issues but this time I've spent almost 3 hours knowing the theory and groping for a proper way to do it but without any improvements.

I tested different approaches but this one seems to be the clearest and the nearest to the solution to me:

I got 2 simple classes that are Vector (only holding reference to X and Y) and Block that holds references such as Position, Velocity for the current motion, nextVelocity to anticipate steering, Previous for the previous block in the snake body.

I initialize my snake like that :

    public Snake(){
    this.head = new Block(250,250);
    for(int i = 0; i < 5; i++){
        if(i == 0){
            body.add(i, new Block(new Vector(head.getPosition().getX() - 18 , head.getPosition().getY())));
            body.get(i).setPrevious(this.head);
        }else{
            body.add(i, new Block(new Vector(body.get(i-1).getPosition().getX() - 18, body.get(i-1).getPosition().getY())));
            body.get(i).setPrevious(body.get(i-1));
        }
    }
    this.head.setPrevious(body.get(0));
}

Then there are my steer() method for the head as well as my follow() method for the other body parts.

I used the variable nextVelocity that stores the velocity of the previous part if this one is different from the current one, in this case nextVelocity is passed to velocity at the next frame to avoid that the whole body moves at the same time in the direction taken by the head (which apparently doesn't work):

public void steer(Vector velocity){
    this.velocity = velocity;
    this.position.add(velocity);
    this.shape.relocate(this.getPosition().getX(), this.getPosition().getY());
}


public void follow(Block previous){
    if(this.velocity == null) {
        this.velocity = new Vector(previous.velocity);
    }else if(this.nextVelocity != null) {
        this.velocity = new Vector(this.nextVelocity);
        this.nextVelocity = null;
    }else if(this.previous.velocity != this.velocity){
        this.nextVelocity = new Vector(this.previous.velocity);
    }
    this.position.add(this.velocity);
    this.shape.relocate(this.getPosition().getX(), this.getPosition().getY());
}

And finally my move() method handling the whole snake moves:

public void move(Vector direction){
    this.head.steer(direction);
    for(int i = 0; i < body.size(); i++){
        if(i == 0){
            body.get(i).follow(this.getHead());
        }else{
            body.get(i).follow(body.get(i-1));
        }
    }
}

If anyone could point me into the right direction or just let me know where I am mistaking it would be amazing.

\$\endgroup\$
  • 2
    \$\begingroup\$ We need to know what this code actually results in, and what you want to happen that is different from the results, to help solve the problem. You can edit your question to include these details. \$\endgroup\$ – Alex F Jun 5 at 0:11
  • 1
    \$\begingroup\$ Sorry I am not used to post on forums. Post edited, I hope it makes sense \$\endgroup\$ – Mat Ertonn Jun 5 at 13:54
  • \$\begingroup\$ Try looping from the tail forward, making each block move to the previous blocks location, before moving it on to the next. When you get to the head you then apply the new direction or the next move. If you're implementing speeding up, you'll need to handle that, but this would ensure that the snakes body always follows the path laid out by the head. \$\endgroup\$ – kendavidson Jun 10 at 18:16
  • \$\begingroup\$ Welcome to the site... this is not a forum. \$\endgroup\$ – Theraot Jun 13 at 6:00
0
\$\begingroup\$

What you need is to store a path.

Keep a collection of positions the head has been. On each tick you will add a new position to the collection. You may also remove positions that are too far back. Because you will be adding and removing, a circular list would do.

How big should the capacity of the circular list be? It needs to be long enough to accommodate the whole snake.

An optimization you should do is to add only if the new position has changed more than some minimun distance form the last one. That will get rid of redundant positions wasting space in the circular list. Also, if you know the minimun distance, you know the capacity you need in the circular list by dividing the length of the snake.


Once you have a path, your job is to place the parts of the snake along the path, keeping their distance. Thus, each tick, you set the position of each segment (iterating from head to tail) a given distance from the previous segment.

This means that the body will follow the same path as the head (a follow steering behaviour will have the segments try to cut ahead). It also means that the velocity of the head will not cause the snake to compress (we can imagine a simple follow steering behaviour will have snake body collapse to the position of the head when it stops) or stretch (if you use a follow steering behaviour the snake will become longer when the head accelerates).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.