Consulta sobre Timer y Garbage Collector

Iniciado por zonahurbana, 2 Agosto 2013, 23:53 PM

0 Miembros y 1 Visitante están viendo este tema.

zonahurbana

Bueno, la verdad es que hasta antes sólo he creado programas pequeños en C++ en consola y algunos otros con interfaz en Windows Forms (Visual Studio 2010).

Como me enteré que en el ciclo entrante llevaré Java pues me he puesto a practicar un poco.
No conozco con exactitud cómo funcionan las clases en Java y tampoco las funciones de las bibliotecas que más suelen usarse... pero apoyado de las advertencias que me daba Netbeans he ido puliendo lo que vendría a ser un juego de la "Snake". Y también basándome un poco en lo que se describe en algunas páginas de referencia.

He conseguido que el programa funcione, es decir, usando las flechas puedo cambiar el sentido de la snake, y esta se desplaza a donde le indique.
Cuando consume un "plus" aumenta su tamaño y reproduce un pequeño sonido (lo mismo hace cuando se estrella contra sí misma). Tiene un sonido de fondo en todo momento, también ...

El .jar puede descargarse de acá:
http://www.mediafire.com/download/e1jvhvph3fa4d38/Snake.jar

Y el código lo copiaré todo a continuación. De seguro se verá un tanto amontonado, porque también incluyo el código generado por el Netbeans cuando se crea un proyecto de Java nuevo y se le agrega un JFrame (y el código que se ha generado mientras yo hacía modificaciones en vista de diseño):
Código (java) [Seleccionar]
package SnakePackage;

import java.applet.AudioClip;
import java.awt.event.ActionEvent; // Complemento para ActionListener.
import java.awt.event.ActionListener; // Para agrupar acciones a desarrollarse.
import java.awt.event.KeyEvent; // Para controlar las pulsaciones del teclado.
import java.util.ArrayList; // Para crear un arreglo dinámico.
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon; // Para añadir imágenes a los JLabel.
import javax.swing.JLabel;
import javax.swing.JOptionPane; // Para mostrar un cuadro de diálogo.
import javax.swing.Timer; // Para controlar el tiempo de desplazamiento.

/**
* @author JCarlos
*/
public class MyMain extends javax.swing.JFrame {

    /**
     * Creates new form MyMain
     */
    public MyMain() {
        this.Sonido_Eat = java.applet.Applet.newAudioClip( getClass().getResource("/SnakePackage/sound/eat.wav") );
        this.Sonido_Fondo = java.applet.Applet.newAudioClip( getClass().getResource("/SnakePackage/sound/music.wav") );
        this.Sonido_Impacto = java.applet.Applet.newAudioClip( getClass().getResource("/SnakePackage/sound/choque.wav") );
        initComponents();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                         
    private void initComponents() {

        opt1 = new javax.swing.JRadioButton();
        opt2 = new javax.swing.JRadioButton();
        opt3 = new javax.swing.JRadioButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Snake - By JCRS");
        setMaximumSize(new java.awt.Dimension(350, 280));
        setMinimumSize(new java.awt.Dimension(350, 280));
        setName("MarcoPrincipal"); // NOI18N
        setResizable(false);
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowOpened(java.awt.event.WindowEvent evt) {
                formWindowOpened(evt);
            }
        });
        addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                formKeyPressed(evt);
            }
        });

        opt1.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
        opt1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                opt1ActionPerformed(evt);
            }
        });

        opt2.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
        opt2.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                opt2ActionPerformed(evt);
            }
        });

        opt3.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
        opt3.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                opt3ActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(18, 18, 18)
                .addComponent(opt1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 104, Short.MAX_VALUE)
                .addComponent(opt2)
                .addGap(92, 92, 92)
                .addComponent(opt3)
                .addGap(73, 73, 73))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap(244, Short.MAX_VALUE)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(opt3)
                    .addComponent(opt2)
                    .addComponent(opt1))
                .addGap(15, 15, 15))
        );

        pack();
    }// </editor-fold>                       

ActionListener MoverIzq = new ActionListener()
{   @Override
    public void actionPerformed(ActionEvent e)
    {
        Body.setLocation(Body.getX()-10, Body.getY());
        ActualizarPosiciones();
        VerificarColision();           
        if(Body.getX()==-10) Body.setLocation(340, Body.getY());
    }
};

ActionListener MoverDer = new ActionListener()
{   @Override
    public void actionPerformed(ActionEvent e)
    {
        Body.setLocation(Body.getX()+10, Body.getY());
        ActualizarPosiciones();
        VerificarColision();
        if(Body.getX()==350) Body.setLocation(0, Body.getY());
    }
};

ActionListener MoverAba = new ActionListener()
{   @Override
    public void actionPerformed(ActionEvent e)
    {
        Body.setLocation(Body.getX(), Body.getY()+10);
        ActualizarPosiciones();
        VerificarColision();
        if(Body.getY()==280) Body.setLocation(Body.getX(), 0);
    }
};

ActionListener MoverArr = new ActionListener()
{   @Override
    public void actionPerformed(ActionEvent e)
    {
        Body.setLocation(Body.getX(), Body.getY()-10);
        ActualizarPosiciones();
        VerificarColision();
        if(Body.getY()==-10) Body.setLocation(Body.getX(), 270);
    }
};

void ActualizarPosiciones()
{
    for(int x=Bloques.size()-1; x>0; --x)
        Bloques.get(x).setLocation(Bloques.get(x-1).getLocation());
    if(Bloques.size()>0)
        Bloques.get(0).setLocation( Body.getLocation() );
    repaint();
}

// ¡Sonidos! Sus direcciones están en el constructor arriba.
AudioClip Sonido_Fondo, Sonido_Eat, Sonido_Impacto;

void VerificarColision()
{
    if(Body.getX() == Plus.getX() && Body.getY() == Plus.getY())
    {
        Sonido_Eat.play();
        JOptionPane.showMessageDialog(new MyMain(), "Ahora el tamaño de la serpiente aumentará, ¡ten cuidado!", "Has conseguido comer un plus.", JOptionPane.INFORMATION_MESSAGE);
        randomizar(Plus);
        CrecerSnake();
    }
   
    for(int i=0; i<Bloques.size()-1; ++i)
        for(int j=i+1; j<Bloques.size(); ++j)
            if(Bloques.get(i).getX()==Bloques.get(j).getX() && Bloques.get(i).getY()==Bloques.get(j).getY())
            {
                Sonido_Impacto.play();
                Perdiste();
            }
}   

void Perdiste()
{
    JOptionPane.showMessageDialog(new MyMain(), "¡Lo siento mucho! Has perdido.\n\tPuntuación: "+Bloques.size(), "Game Over.", JOptionPane.INFORMATION_MESSAGE);
    // randomizar(Plus);
    Temporizador.stop();
    for(int i=0; i<Bloques.size(); ++i)
        this.getContentPane().remove(Bloques.get(i));
    Bloques.clear();
    this.getContentPane().remove( Body );
    this.getContentPane().remove( Plus );
    Menu.setVisible(true);
    opt1.setVisible(true);
    opt2.setVisible(true);
    opt3.setVisible(true);
   
}

        ArrayList<JLabel> Bloques = new ArrayList<>(); // Creamos el arreglo vacío.

void CrecerSnake()
{
    JLabel Nuevo = new JLabel( new ImageIcon(getClass().getResource("/SnakePackage/imgs/body.gif")) );
    //Nuevo.setLocation(0, 0);
    Nuevo.setSize(10, 10);
    Bloques.add( Nuevo );
    this.getContentPane().add( Bloques.get( Bloques.size()-1 ) );       
}

Timer Temporizador = new Timer(1000, null); // int delay, ActionListener listener
           
    private void formKeyPressed(java.awt.event.KeyEvent evt) {                               
        if(
             (evt.getKeyCode() == KeyEvent.VK_LEFT
             || evt.getKeyCode() == KeyEvent.VK_RIGHT
             || evt.getKeyCode() == KeyEvent.VK_DOWN
             || evt.getKeyCode() == KeyEvent.VK_UP
             || evt.getKeyCode() == KeyEvent.VK_V)
             && Temporizador.getActionListeners().length > 0
           )
            Temporizador.removeActionListener(Temporizador.getActionListeners()[0]);
        if(evt.getKeyCode() == KeyEvent.VK_LEFT)
        {
            Temporizador.addActionListener(MoverIzq);
            //JOptionPane.showMessageDialog(this, "Has presionado la tecla con una flecha hacia la izquierda.", "Tecla presionada.", JOptionPane.INFORMATION_MESSAGE);
        }
        if(evt.getKeyCode() == KeyEvent.VK_RIGHT)
        {
            Temporizador.addActionListener(MoverDer);
        }
        if(evt.getKeyCode() == KeyEvent.VK_DOWN)
        {
            Temporizador.addActionListener(MoverAba);
        }
        if(evt.getKeyCode() == KeyEvent.VK_UP)
        {
            Temporizador.addActionListener(MoverArr);
        }
        if(evt.getKeyCode() == KeyEvent.VK_V)
        { // Muestra las posiciones.
            JOptionPane.showMessageDialog(new MyMain(), "Posición de Body: "+Body.getX()+", "+Body.getY()+"\nPosición de Plus: "+Plus.getX()+", "+Plus.getY(), "Game Over.", JOptionPane.CLOSED_OPTION);
        }
    }                               

int XAleatorio()
{
    return (int)(Math.random() * (34 + 1))*10;
}
int YAleatorio()
{
    return (int)(Math.random() * (27 + 1))*10;
}

void randomizar(JLabel L)
{
    L.setLocation(XAleatorio(), YAleatorio());
}

    JLabel Body = new JLabel( new ImageIcon(getClass().getResource("/SnakePackage/imgs/body.gif")) );
    JLabel Plus = new JLabel( new ImageIcon(getClass().getResource("/SnakePackage/imgs/plus.png")) );
   
    void EmpezarJuego(int velocidad)
    {     
        // Limpiamos componentes del menú.
        Menu.setVisible(false);
        opt1.setVisible(false);
        opt2.setVisible(false);
        opt3.setVisible(false);
       
        Temporizador.setDelay(velocidad);
        Temporizador.start();
       
        Body.setSize(10, 10); Plus.setSize(10, 10);
       
        this.getContentPane().add( Body );
        this.getContentPane().add( Plus );
       
        randomizar(Body);
        randomizar(Plus);
    }
   
    JLabel Menu = new JLabel( new ImageIcon(getClass().getResource("/SnakePackage/imgs/Start.jpg")) );
       
    private void formWindowOpened(java.awt.event.WindowEvent evt) {                                 
        Sonido_Fondo.loop();
       
        Menu.setSize(350, 280);
        this.getContentPane().add(Menu);
        Menu.setLocation(0, 0); repaint();
       
        ButtonGroup grupoBotonesOpcion = new ButtonGroup();
        grupoBotonesOpcion.add( opt1 );
        grupoBotonesOpcion.add( opt2 );
        grupoBotonesOpcion.add( opt3 );
    }                                 

    private void opt1ActionPerformed(java.awt.event.ActionEvent evt) {                                     
        if( JOptionPane.showConfirmDialog(null, "¿Desea jugar en modo fácil?", "Confirme su petición.", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.YES_OPTION)
        {
            EmpezarJuego(120);
        }
    }                                   

    private void opt2ActionPerformed(java.awt.event.ActionEvent evt) {                                     
        if( JOptionPane.showConfirmDialog(null, "¿Desea jugar en modo medio dificultoso?", "Confirme su petición.", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.YES_OPTION)
        {
            EmpezarJuego(80);
        }
    }                                   

    private void opt3ActionPerformed(java.awt.event.ActionEvent evt) {                                     
        if( JOptionPane.showConfirmDialog(null, "¿Desea jugar en modo difícil?", "Confirme su petición.", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.YES_OPTION)
        {
            EmpezarJuego(60);
        }        // TODO add your handling code here:
    }                                   

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(MyMain.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(MyMain.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(MyMain.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(MyMain.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new MyMain().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify                     
    private javax.swing.JRadioButton opt1;
    private javax.swing.JRadioButton opt2;
    private javax.swing.JRadioButton opt3;
    // End of variables declaration                   
}


¿Cuál es el problema?
Bueno quisiera que me ayuden a entender las razones de por qué el programa se detiene repentinamente luego que se han consumido unas 12-14 veces el plus (la canción de fondo sigue reproduciéndose pero la Snake no obedece, es decir, no se desplaza por más que presione las flechas del teclado).
En el título he puesto Garbage Collector porque no se me ocurría otra cosa que pudiera detener el funcionamiento... pero la verdad es que tampoco estoy muy seguro de esto, pues C++ no tiene un recolector de basura por defecto. ¿Será que el recolector termina por borrar el Timer y por ello ya no ejecuta las acciones? Porque el programa no es que se haya "colgado", sino que las acciones de desplazamiento es como si hubiera dejado de existir.

Gracias y espero puedan orientarme un poco.
Nunca dejar de aprender es importante, más allá del ritmo que se siga ...