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
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 */ /** * 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() { setTitle("Snake - By JCRS"); setName("MarcoPrincipal"); // NOI18N setResizable(false); formWindowOpened(evt); } }); formKeyPressed(evt); } }); opt1ActionPerformed(evt); } }); opt2ActionPerformed(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) .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() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(opt3) .addComponent(opt2) .addComponent(opt1)) .addGap(15, 15, 15)) ); pack(); }// </editor-fold> { @Override { Body.setLocation(Body.getX()-10, Body.getY()); ActualizarPosiciones(); VerificarColision(); if(Body.getX()==-10) Body.setLocation(340, Body.getY()); } }; { @Override { Body.setLocation(Body.getX()+10, Body.getY()); ActualizarPosiciones(); VerificarColision(); if(Body.getX()==350) Body.setLocation(0, Body.getY()); } }; { @Override { Body.setLocation(Body.getX(), Body.getY()+10); ActualizarPosiciones(); VerificarColision(); if(Body.getY()==280) Body.setLocation(Body.getX(), 0); } }; { @Override { 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 ); opt1.setVisible(true); opt2.setVisible(true); opt3.setVisible(true); } ArrayList<JLabel> Bloques = new ArrayList<>(); // Creamos el arreglo vacío. void CrecerSnake() { //Nuevo.setLocation(0, 0); Nuevo.setSize(10, 10); Bloques.add( Nuevo ); this.getContentPane().add( Bloques.get( Bloques.size()-1 ) ); } if( && Temporizador.getActionListeners().length > 0 ) Temporizador.removeActionListener(Temporizador.getActionListeners()[0]); { Temporizador.addActionListener(MoverIzq); //JOptionPane.showMessageDialog(this, "Has presionado la tecla con una flecha hacia la izquierda.", "Tecla presionada.", JOptionPane.INFORMATION_MESSAGE); } { Temporizador.addActionListener(MoverDer); } { Temporizador.addActionListener(MoverAba); } { Temporizador.addActionListener(MoverArr); } { // 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() { } int YAleatorio() { } { L.setLocation(XAleatorio(), YAleatorio()); } void EmpezarJuego(int velocidad) { // Limpiamos componentes del menú. 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); } Sonido_Fondo.loop(); grupoBotonesOpcion.add( opt1 ); grupoBotonesOpcion.add( opt2 ); grupoBotonesOpcion.add( opt3 ); } if( JOptionPane.showConfirmDialog(null, "¿Desea jugar en modo fácil?", "Confirme su petición.", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.YES_OPTION) { EmpezarJuego(120); } } if( JOptionPane.showConfirmDialog(null, "¿Desea jugar en modo medio dificultoso?", "Confirme su petición.", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.YES_OPTION) { EmpezarJuego(80); } } 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 */ /* 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())) { break; } } java.util.logging.Logger.getLogger(MyMain.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); java.util.logging.Logger.getLogger(MyMain.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); java.util.logging.Logger.getLogger(MyMain.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); java.util.logging.Logger.getLogger(MyMain.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } //</editor-fold> /* Create and display the form */ public void run() { new MyMain().setVisible(true); } }); } // Variables declaration - do not modify // 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.