Search code examples
javaswingcustom-painting

Snake game in Java Swing - My snake only grows


I'm trying to do the classic Snake game in Swing and I managed to make Snake move, but when it moves, it becomes infinitely long because it never erases its tail.

I have been using validate() and repaint() but nothing works.

Problem

This is my code:

import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;

public class controls extends JPanel implements KeyListener, ActionListener { // Amb aquestes dues implementacions, fem que el programa pugui rebre per teclat

// Mida serp
private int[] longXserp = new int[750];
private int[] longYserp = new int[750];

// Longitut inicial de la serp
private int serplong = 3;

// Moviments que fem
private int moviments = 0;

// Controls
private boolean esquerra = false;
private boolean dreta = false;
private boolean amunt = false;
private boolean avall = false;

// Gràfics de moviment
private ImageIcon serpesquerra;
private ImageIcon serpdreta;
private ImageIcon serpamunt;
private ImageIcon serpavall;

private Timer timer;
private int velocitatserp = 100;
private ImageIcon serp;

public controls() {
    addKeyListener(this);
    setFocusable(true);
    setFocusTraversalKeysEnabled(false);
    timer = new Timer(velocitatserp, this);
    timer.start();
}

public void paint(Graphics g) { // Mètode amb el que imprimim per pantalla. Ha d'anomenar-se "paint", o si no, no funcionarà

    if(moviments == 0) {

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

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


    serpdreta = new ImageIcon("src/grafics/serpdreta.png");
    serpdreta.paintIcon(this, g, longXserp[0], longYserp[0]);

    for(int a = 0; a < serplong; a++) {

        if(a == 0 && esquerra) {

            serpesquerra = new ImageIcon("src/grafics/serpesquerra.png");
            serpesquerra.paintIcon(this, g, longXserp[a], longYserp[a]);
        }

        if(a == 0 && dreta) {

            serpdreta = new ImageIcon("src/grafics/serpdreta.png");
            serpdreta.paintIcon(this, g, longXserp[a], longYserp[a]);
        }

        if(a == 0 && amunt) {

            serpamunt = new ImageIcon("src/grafics/serpamunt.png");
            serpamunt.paintIcon(this, g, longXserp[a], longYserp[a]);
        }

        if(a == 0 && avall) {

            serpavall = new ImageIcon("src/grafics/serpavall.png");
            serpavall.paintIcon(this, g, longXserp[a], longYserp[a]);
        }

        if(a != 0) {
            serp = new ImageIcon("src/grafics/serp.png");
            serp.paintIcon(this, g, longXserp[a], longYserp[a]);
        }

    }

    g.dispose();
}


@Override
public void keyPressed(KeyEvent ke) {

    if(ke.getKeyCode() == KeyEvent.VK_RIGHT){ // Si polses la tecla X, la seva variable boolean es posa en true
        moviments++;
        dreta = true;

        if(!esquerra) {
            dreta = true;
        }

        else {
            dreta = false;
            esquerra = true;
        }

        amunt = false;
        avall = false;
    }

    if(ke.getKeyCode() == KeyEvent.VK_LEFT){
        moviments++;
        esquerra = true;

        if(!dreta) {
            esquerra = true;
        }

        else {
            esquerra = false;
            dreta = true;
        }

        amunt = false;
        avall = false;
    }

    if(ke.getKeyCode() == KeyEvent.VK_UP){
        moviments++;
        amunt = true;

        if(!avall) {
            amunt = true;
        }

        else {
            amunt = false;
            avall = true;
        }

        esquerra = false;
        dreta = false;
    }

    if(ke.getKeyCode() == KeyEvent.VK_DOWN){
        moviments++;
        avall = true;

        if(!amunt) {
            avall = true;
        }

        else {
            amunt = true;
            avall = false;
        }

        esquerra = false;
        dreta = false;
    }

}
@Override
public void keyReleased(KeyEvent ke) {
    // TODO Auto-generated method stub

}
@Override
public void keyTyped(KeyEvent ke) {
    // TODO Auto-generated method stub

}
@Override
public void actionPerformed(ActionEvent ae) {

    timer.start();

    if(dreta) {

        for(int d = serplong-1; d>=0; d--) {
            longYserp[d+1] = longYserp[d];
        }

        for(int e = serplong; e>= 0; e--) {
            if(e==0) {
                longXserp[e] = longXserp[e] + 25;
            }

            else {
                longXserp[e] = longXserp[e-1];
            }

            if(longXserp[e] > 850) {
                longXserp[e] = 25;

            }
        }

        repaint(); // Mètode per refrescar els gràfics tornant a cridar al mètode paint(). Es usa quan es realitzan canvis sobre els gràfics
    }

    if(esquerra) {

        for(int d = serplong-1; d>=0; d--) {
            longYserp[d+1] = longYserp[d];
        }

        for(int d = serplong; d>= 0; d--) {
            if(d==0) {
                longXserp[d] = longXserp[d] - 25;
            }

            else {
                longXserp[d] = longXserp[d-1];
            }

            if(longXserp[d] < 25) {
                longXserp[d] = 850;

            }
        }

        repaint();
    }

    if(amunt) {

        for(int d = serplong-1; d>=0; d--) {
            longXserp[d+1] = longXserp[d];
        }

        for(int d = serplong; d>= 0; d--) {
            if(d==0) {
                longYserp[d] = longYserp[d] - 25;
            }

            else {
                longYserp[d] = longYserp[d-1];
            }

            if(longYserp[d] < 75) {
                longYserp[d] = 625;

            }
        }

        repaint();
    }

    if(avall) {

        for(int d = serplong-1; d>=0; d--) {
            longXserp[d+1] = longXserp[d];
        }

        for(int d = serplong; d>= 0; d--) {
            if(d==0) {
                longYserp[d] = longYserp[d] + 25;
            }

            else {
                longYserp[d] = longYserp[d-1];
            }

            if(longYserp[d] > 625) {
                longYserp[d] = 75;

            }
        }

        repaint();
    }
}   
}

I've been trying to figure out what's wrong for days. What is wrong with the above code?


Solution

  • public void paint(Graphics g) { ..
    

    This should be:

    public void paintComponent(Graphics g) { 
        super.paintComponent(g); ..
    

    Note two important differences:

    1. Override paintComponent for any class that is or extends JComponent.
    2. Call the super method when custom painting to erase the previous drawings.

    Other tips now I look more closely at the code:

    1. serpdreta = new ImageIcon("src/grafics/serpdreta.png"); This type of resource loading should not be done in a paint method, which are expected to be completed in a minimal time. Instead load the image(s) when the class is constructed and store them as fields of the class, to then be used whenever needed.
    2. But that path will likely fail when the app. is turned into a Jar. The src directory is typically not included.
    3. Application resources will become embedded resources by the time of deployment, so it is wise to start accessing them as if they were, right now. An must be accessed by URL rather than file. See the info. page for embedded resource for how to form the URL.
    4. If the app. needs to paint images, create an Image rather than an ImageIcon.
    5. For better feedback when loading images, use ImageIO.read(..) rather than creating an ImageIcon as it will fail silently while ImageIO will provide useful information as to the cause of any problems.
    6. For Swing, we typically use key bindings rather than the lower level KeyListener.