1

This is my first query here. I am building a snake game which runs perfectly but the body moves only three steps and pauses. The following is the code for the gameplay, i have the main method, but its absolutely fine. Image : before pressing right arrow........... Image : after pressing right arrow

Main Method :

public class Snake {

public static void main(String[] args){

    Gameplay gplay = new Gameplay();
    JFrame obj = new JFrame();
    obj.setTitle("Super SNake");
    obj.setBounds(10, 10, 905, 700);
    obj.setBackground(Color.getHSBColor(192, 68, 66));
    obj.setResizable(false);
    obj.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    obj.add(gplay);
    obj.setVisible(true);
}

}

Gameplay code :

public class Gameplay extends JPanel implements KeyListener, ActionListener{

private int[] snakeXlength = new int[750];
private int[] snakeYlength = new int[750];
private int lengthsnake = 3;
private int moves = 0;

private boolean left = false;
private boolean right = false;
private boolean up = false;
private boolean down = false;

private ImageIcon umouth;
private ImageIcon lmouth;
private ImageIcon rmouth;
private ImageIcon dmouth;

private ImageIcon body;
private ImageIcon imagetitle;

private Timer t;
private int delay = 100;

public Gameplay(){

    addKeyListener(this);
    setFocusable(true);
    setFocusTraversalKeysEnabled(false);
    t = new Timer(delay,this);
    t.start();

}

public void paint(Graphics g){
    if(moves==0){

        snakeXlength[2] = 50;
        snakeXlength[1] = 75;
        snakeXlength[0] = 100;

        snakeYlength[2] = 100;
        snakeYlength[1] = 100;
        snakeYlength[0] = 100;
    }


    //title border
    g.setColor(Color.black);
    g.drawRect(24, 10, 851, 55);
    //title
    imagetitle = new ImageIcon( getClass().getResource("title.jpg"));
    imagetitle.paintIcon(this, g, 25, 11);
    //play area
    g.setColor(Color.black);
    g.drawRect(24, 74, 851, 577);
    //bg for play area
    g.setColor(Color.black);
    g.fillRect(25, 75, 850, 575);
    rmouth = new ImageIcon( getClass().getResource("rmouth.png") );
    //rmouth = new ImageIcon("game/rmouth.png");
    rmouth.paintIcon(this, g, snakeXlength[0], snakeYlength[0]);

    for(int a=0; a<lengthsnake; a++){
        if(a==0 && right){
        rmouth = new ImageIcon( getClass().getResource("rmouth.png") );
        //rmouth = new ImageIcon("rmouth.png");
        rmouth.paintIcon(this, g, snakeXlength[a], snakeYlength[a]);
        }
        if(a==0 && left){
        lmouth = new ImageIcon( getClass().getResource("lmouth.png") );
        //lmouth = new ImageIcon("lmouth.png");
        lmouth.paintIcon(this, g, snakeXlength[a], snakeYlength[a]);
        }
        if(a==0 && up){
        umouth = new ImageIcon( getClass().getResource("umouth.png") );
        //umouth = new ImageIcon("umouth.png");
        umouth.paintIcon(this, g, snakeXlength[a], snakeYlength[a]);
        }
        if(a==0 && down){
        dmouth = new ImageIcon( getClass().getResource("dmouth.png") );
        //dmouth = new ImageIcon("dmouth.png");
        dmouth.paintIcon(this, g, snakeXlength[a], snakeYlength[a]);
        }
        if(a!=0){
        body = new ImageIcon( getClass().getResource("body.png") );
        //body = new ImageIcon("body.png");
        body.paintIcon(this, g, snakeXlength[a], snakeYlength[a]);
        }
        }
        g.dispose();
}

@Override
public void keyTyped(KeyEvent e) {

}

@Override
public void keyPressed(KeyEvent e) {
     if(e.getKeyCode()== KeyEvent.VK_RIGHT){
    moves++;
    right= true;
    if(!left){
     right=true;
    }
    else{
    right= false;
    left= true;
    }
}

if(e.getKeyCode()== KeyEvent.VK_LEFT){
 moves++;
left= true;
if(!right){
left=true;
}
else{
 left= false;
 right= true;
}
up=false;
down=false;
}
if(e.getKeyCode()== KeyEvent.VK_UP){
moves++;
up= true;
if(!down){
up=true;
}
else{
up= false;
down= true;
}

left=false;
right=false;
}

if(e.getKeyCode()== KeyEvent.VK_DOWN){
moves++;
down= true;
if(!up){
down=true;
}
else{
down= false;
up= true;
}

left=false;
right=false;
}

}

@Override
public void keyReleased(KeyEvent e) {

}

@Override
public void actionPerformed(ActionEvent e) {
    t.start();

if(right){

for(int r= lengthsnake-1; r>=0; r--){
snakeYlength[r+1]= snakeYlength[r];
}
for(int r=lengthsnake; r>0; r--){
if(r==0){
 snakeXlength[r]= snakeXlength[r]+25;
}
else{
 snakeXlength[r]= snakeXlength[r-1];
}

if(snakeXlength[r]> 850){
 snakeXlength[r]= 25;
}
}

repaint();
}

    if(left){

    for(int r= lengthsnake-1; r>=0; r--){
snakeYlength[r+1]= snakeYlength[r];
}
for(int r=lengthsnake; r>0; r--){
if(r==0){
 snakeXlength[r]= snakeXlength[r] -25;
}
else{
 snakeXlength[r]= snakeXlength[r-1];
}

if(snakeXlength[r]< 25){
 snakeXlength[r]= 850;
}
}

repaint();
    }
    if(up){

    for(int r= lengthsnake-1; r>=0; r--){
snakeXlength[r+1]= snakeXlength[r];
}
for(int r=lengthsnake; r>0; r--){
if(r==0){
 snakeYlength[r]= snakeYlength[r]+25;
}
else{
 snakeYlength[r]= snakeYlength[r-1];
}

if(snakeYlength[r]< 625){
 snakeYlength[r]= 75;
}
}

repaint();
}
    if(down){

    for(int r= lengthsnake-1; r>=0; r--){
snakeXlength[r+1]= snakeXlength[r];
}
for(int r=lengthsnake; r>0; r--){
if(r==0){
 snakeYlength[r]= snakeYlength[r] +25;
}
else{
 snakeYlength[r]= snakeYlength[r-1];
}

if(snakeYlength[r] > 625){
 snakeYlength[r]= 75;
}
}

repaint();
    }

    }

}

I hope you will be able to help me with my problem. I don't know where i am wrong. The snake moves but stops after 3 steps, its still changing direction of head but in that position only. This is my first query here in stack overflow. I hope the above data helps to understand the problem. Please help me to find the solution.

Ankit Rath
  • 59
  • 7
  • 2
    1) For better help sooner, post a [MCVE] or [Short, Self Contained, Correct Example](http://www.sscce.org/). 2) Use a logical and consistent form of indenting code lines and blocks. The indentation is intended to make the flow of the code easier to follow! – Andrew Thompson Jul 07 '18 at 07:45
  • 1
    The first 'non generic' tips I'd offer are: 1) Don't forget to ask a question. 2) Images should be loaded once & stored as class attributes. 3) For the MCVE / SSCCE, one way to get image(s) for an example is to hot link to images seen in [this Q&A](http://stackoverflow.com/q/19209650/418556). 4) `public void paint(Graphics g){` For any `JComponent`, the correct method is `paintComponent(Graphics)` - always call the super method first, either way. 5) Use `@Override` notation whenever changing method functionality. It is a compilation sanity check for correct method spelling & signature. – Andrew Thompson Jul 07 '18 at 07:47
  • Your mashing logic in both the `paint` and `ActionListener`, this is bad idea, ALL your logic should exist in the `ActionListener` and the `paint` method should paint the current state – MadProgrammer Jul 07 '18 at 08:19
  • You also never "release" any key state, so it's repeatedly going around through the key state processing endlessly – MadProgrammer Jul 07 '18 at 08:26

1 Answers1

0

So, there are a number of things "wrong", but I'll start with the most obvious issue.

Look only at the right direction handler...

for (int r = lengthsnake; r > 0; r--) {
    if (r == 0) {
        snakeXlength[r] = snakeXlength[r] + 25;
    } else {
        snakeXlength[r] = snakeXlength[r - 1];
    }

    if (snakeXlength[r] > 850) {
        snakeXlength[r] = 25;
    }
}

There is no way that r can be equal to 0 in this loop, which means, the 0 element never changes.

It should be more like...

for (int r = lengthsnake; r > 0; r--) {
    snakeXlength[r] = snakeXlength[r - 1];

    if (snakeXlength[r] > 850) {
        snakeXlength[r] = 25;
    }
}
snakeXlength[0] = snakeXlength[0] + 25;
if (snakeXlength[0] > 850) {
    snakeXlength[0] = 25;
}

I suspect all the other "direction" handlers have the same issue.

Next...

@Override
public void actionPerformed(ActionEvent e) {
    t.start();
    //...

Starting the timer in it's own ActionListener probably isn't the best idea. A Swing Timer will repeat by itself automatically.

Next...

public void paint(Graphics g) {
    if (moves == 0) {
    //...
    g.dispose();
}

is a bag of bad ideas.

First, as general recommendation, you should avoid overriding paint. Instead, you should focus on overriding paintComponent.

You should also call the paint method's super, to maintain the paint chain.

You should never dispose of a Graphics context you didn't create yourself, it can have adverse effects on other things which need to be painted after your component.

Instead, something more like...

protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    if (moves == 0) {
    //...
}

Next...

I've given up even discussing KeyListener - instead, you should be using How to Use Key Bindings

Next...

You never release any of the key states, this is going to be very problematic and the direction of the head should actually be managed separately from the key state and the paint process

Suggestions...

Use a different data structure to manage your snake. Something like a LinkedList would be find if you only need linear access (or ArrayList if you need random access). The point is, you can easily insert a new element at the "head" of the List with having to "copy" all the old elements over one place.

I would create two more elements. One to manage the "type" of element and one to manage the location of the element.

public enum SnakePartType {
    LEFT_HEAD, RIGHT_HEAD, DOWN_HEAD, UP_HEAD, BODY
}

public class SnakePart {
    private SnakePartType type;
    private Point location; // You could use separate x/y properties, but Point already does it

    public SnakePart(SnakePartType type, Point location) {
        this.type = type;
        this.location = location;
    }

    // Getters for type and location
}

This way, your paint process doesn't need to know about the key state, which will change between paint passes and can rely on the information in the model instead

Community
  • 1
  • 1
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366