java swing animation sprite

Java Swing Timer y Animación: cómo armarlo



animation sprite (1)

Voy a volver a publicar esta pregunta nuevamente tratando de ser más precisa y esperando obtener ayuda porque esto me está volviendo loco. Estoy desarrollando un juego de mesa con hasta 6 jugadores, cada uno con un peón de diferente color. Tengo la siguiente imagen que se carga en matrices BufferedImage que lo trata como un sprite:

y este es el código relativo, colocando cada cara de cada dado de color en una posición en la BufferedImage []:

private BufferedImage[] initAnimationBuffer() { BufferedImage[] result = new BufferedImage[36]; for (int i = 0; i < 6; i++) { for (int j = i; j < 6 + i; j++) result[i + j] = DieSprite.getSprite(j, i, 0); } return result; }

Luego, cada jugador, de acuerdo con su color, tendrá también la siguiente matriz que contiene las caras de su color de acuerdo con el valor / posición del dado obtenido. En otras palabras, esta matriz contiene "una línea" de la imagen y está indexada por valor:

private BufferedImage[][] initExactDieFaces() { BufferedImage[][] result = new BufferedImage[6][1]; int row = -1; String myColor = this.coreGame.getMyPartecipant().getColor(); if (myColor.equals(Constants.COLOR[0])) { row = 0; } else if (myColor.equals(Constants.COLOR[1])) { row = 2; } else if (myColor.equals(Constants.COLOR[2])) { row = 4; } else if (myColor.equals(Constants.COLOR[3])) { row = 1; } else if (myColor.equals(Constants.COLOR[4])) { row = 5; } else if (myColor.equals(Constants.COLOR[5])) { row = 3; } int offset = 0; for (int i = 0; i < 6; i++) { result[i][0] = DieSprite.getSprite(row, i, offset); offset += 2; } return result; }

Lo que quiero es lo siguiente: -cuando se presiona el botón "voltear dado", quiero que (por ejemplo) se muestren 20 caras de dado al azar (deben tomarse de la primera matriz, AnimationBuffer) en un JLabel específico dentro de un JPanel -Tan pronto como la animación anterior haya terminado, quiero que se muestre el resultado obtenido del lanzamiento del dado (según el peón de color, tomado de ExcatDieFaces).

Para obtener esto, sé que necesito Swing Timer, pero no puedo armarlo todo; aquí hay un código del método startAnimationDie que se llama cuando se presiona el botón "flip die":

private void startAnimationDie(final JPanel dieContainer) { final BufferedImage[] animationBuffer = initAnimationBuffer(); final BufferedImage[][] exactDieFaces = initExactDieFaces(); final AnimationSprite animation = new AnimationSprite( animationBuffer, Constants.DIE_ANIMATION_SPEED); /* getting launch value fromt the core Game */ int launchResult = coreGame.launchDie(); coreGame.getMyPartecipant().setLastLaunch(launchResult); final Timer timer = new Timer(250, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { dieContainer.removeAll(); dieContainer.updateUI(); animation.start(); JLabel resultDie = new JLabel(); resultDie.setBounds(60, 265, Constants.DIE_SIZE,Constants.DIE_SIZE); resultDie.setIcon(new ImageIcon(animationBuffer[new Random().nextInt(36)])); dieContainer.add(resultDie); dieContainer.updateUI(); updateUI(); repaint(); } }); /* animation begins, rolling faces are shown each time the Timer ends*/ for(int i = 0; i<20; i++) timer.start() /* showing the final face according to the pawn color and the obtained result from the launch */ dieContainer.removeAll(); dieContainer.updateUI(); AnimationSprite resultAnimation = new AnimationSprite(exactDieFaces[launchResult - 1], 6); resultAnimation.start(); resultAnimation.update(); resultDie.setIcon(new ImageIcon(exactDieFaces[launchResult - 1][0])); resultDie.setBounds(60, 265, Constants.DIE_SIZE, Constants.DIE_SIZE); dieContainer.add(resultDie); dieContainer.updateUI(); dieContainer.repaint(); }

¿Cómo puedo hacer que funcione? Creo que se supone que debo usar Swing.invokeAndWait pero no puedo juntar todas las piezas ... ¿Pueden ayudarme por favor?


  1. No llame a updateUI , a menos que esté tratando de instalar una apariencia, no está haciendo lo que cree que es (y es muy ineficiente)
  2. No reconstruya la IU cada vez, esto lleva mucho tiempo, lo que hará que la animación se vea apagada y escalonada y probablemente parpadee mucho. En cambio, simplemente actualice la propiedad del icon de la etiqueta
  3. Use un solo Timer , permita que incremente un contador, para que sepa cuántas veces se ha llamado y actualice la tirada y el contador en cada marca.

Piense en el Timer como un tipo de bucle, donde en cada iteración (marca), debe hacer algo (como incrementar el contador)

(Nota: cuando parece que el dado se ha "estancado", es porque la imagen se muestra más de una vez en secuencia. Puede superar esto colocando todas las imágenes en una List y usando Collections.shuffle . Haga esto tres veces , agregar el resultado a otra List debería proporcionarle 24 secuencias sin repetición (está bien, "podría" repetirse en los límites, pero es mejor que usar Math.random ;))

import java.awt.Dimension; import java.awt.EventQueue; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class Test { public static void main(String[] args) { new Test(); } public Test() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(new TestPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class TestPane extends JPanel { private BufferedImage[] dice = new BufferedImage[6]; private JLabel die; public TestPane() { try { BufferedImage img = ImageIO.read(new File("/Users/swhitehead/Documents/Die.png")); int width = 377 / 6; for (int index = 0; index < 6; index++) { dice[index] = img.getSubimage(width * index, 0, width, width); } } catch (IOException ex) { ex.printStackTrace(); } setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridwidth = GridBagConstraints.REMAINDER; die = new JLabel(new ImageIcon(dice[0])); add(die, gbc); JButton roll = new JButton("Roll"); add(roll, gbc); roll.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { roll.setEnabled(false); Timer timer = new Timer(250, new ActionListener() { private int counter; private int lastRoll; @Override public void actionPerformed(ActionEvent e) { if (counter < 20) { counter++; lastRoll = (int)(Math.random() * 6); System.out.println(counter + "/" + lastRoll); die.setIcon(new ImageIcon(dice[lastRoll])); } else { lastDieRollWas(lastRoll); ((Timer)e.getSource()).stop(); roll.setEnabled(true); } } }); timer.start(); } }); } protected void lastDieRollWas(int roll) { System.out.println("You rolled " + (roll + 1)); } @Override public Dimension getPreferredSize() { return new Dimension(200, 200); } } }