pelota para objetos movimientos mover imagenes graficos fuente figuras codigo animación animaciones animacion java animation timer graphics2d smooth

para - mover figuras en java



¿Cómo hacer que la animación de línea sea más suave? (2)

Hice esta pequeña prueba y no tuve problemas importantes, básicamente era capaz de mantener 50 fps incluso con 1000 rectángulos, todos moviéndose a velocidades aleatorias en direcciones aleatorias.

public class SimpleAnimationEngine { public static void main(String[] args) { new SimpleAnimationEngine(); } public SimpleAnimationEngine() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException ex) { } catch (InstantiationException ex) { } catch (IllegalAccessException ex) { } catch (UnsupportedLookAndFeelException ex) { } AnimationPane pane = new AnimationPane(); JFrame frame = new JFrame("Test"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(pane); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); pane.init(); pane.start(); } }); } public static class AnimationPane extends JPanel implements AnimationCanvas { private AnimationModel model; @Override public Dimension getPreferredSize() { return new Dimension(400, 400); } public AnimationModel getModel() { return model; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g.create(); for (Animatable animatable : getModel().getAnimatables()) { animatable.paint(g2d); } g2d.dispose(); } @Override public synchronized void updateState() { Runnable update = new Runnable() { @Override public void run() { AnimationModel model = getModel(); for (Animatable animatable : model.getAnimatables()) { animatable.copy(); } repaint(); } }; if (EventQueue.isDispatchThread()) { update.run(); } else { try { EventQueue.invokeAndWait(update); } catch (InterruptedException | InvocationTargetException ex) { ex.printStackTrace(); } } } public void init() { model = new DefaultAnimationModel(); for (int index = 0; index < 1000; index++) { model.add(new AnimatableRectangle(this)); } updateState(); } public void start() { AnimationEngine engine = new AnimationEngine(this, getModel()); engine.start(); } } public static interface Animatable { public void copy(); public void update(AnimationCanvas canvas, float progress); public void paint(Graphics2D g2d); } public static class AnimationEngine extends Thread { private AnimationModel model; private AnimationCanvas canvas; public AnimationEngine(AnimationCanvas canvas, AnimationModel model) { setDaemon(true); setName("AnimationThread"); this.model = model; this.canvas = canvas; } public AnimationCanvas getCanvas() { return canvas; } public AnimationModel getModel() { return model; } @Override public void run() { float progress = 0; long cylceStartTime = System.currentTimeMillis(); long cylceEndTime = cylceStartTime + 1000; int updateCount = 0; while (true) { long frameStartTime = System.currentTimeMillis(); getModel().update(getCanvas(), progress); getCanvas().updateState(); long frameEndTime = System.currentTimeMillis(); long delay = 20 - (frameEndTime - frameStartTime); if (delay > 0) { try { sleep(delay); } catch (InterruptedException ex) { } } long now = System.currentTimeMillis(); long runtime = now - cylceStartTime; progress = (float)runtime / (float)(1000); updateCount++; if (progress > 1.0) { progress = 0f; cylceStartTime = System.currentTimeMillis(); cylceEndTime = cylceStartTime + 1000; System.out.println(updateCount + " updates in this cycle"); updateCount = 0; } } } } public interface AnimationCanvas { public void updateState(); public Rectangle getBounds(); } public static interface AnimationModel { public void update(AnimationCanvas canvas, float progress); public void add(Animatable animatable); public void remove(Animatable animatable); public Animatable[] getAnimatables(); } public static class AnimatableRectangle implements Animatable { private Rectangle bounds; private int dx, dy; private Rectangle copyBounds; private Color foreground; private Color backColor; public AnimatableRectangle(AnimationCanvas canvas) { bounds = new Rectangle(10, 10); Rectangle canvasBounds = canvas.getBounds(); bounds.x = canvasBounds.x + ((canvasBounds.width - bounds.width) / 2); bounds.y = canvasBounds.y + ((canvasBounds.height - bounds.height) / 2); dx = (getRandomNumber(10) + 1) - 5; dy = (getRandomNumber(10) + 1) - 5; dx = dx == 0 ? 1 : dx; dy = dy == 0 ? 1 : dy; foreground = getRandomColor(); backColor = getRandomColor(); } protected int getRandomNumber(int range) { return (int) Math.round(Math.random() * range); } protected Color getRandomColor() { return new Color(getRandomNumber(255), getRandomNumber(255), getRandomNumber(255)); } @Override public void copy() { copyBounds = new Rectangle(bounds); } @Override public void update(AnimationCanvas canvas, float progress) { bounds.x += dx; bounds.y += dy; Rectangle canvasBounds = canvas.getBounds(); if (bounds.x + bounds.width > canvasBounds.x + canvasBounds.width) { bounds.x = canvasBounds.x + canvasBounds.width - bounds.width; dx *= -1; } if (bounds.y + bounds.height > canvasBounds.y + canvasBounds.height) { bounds.y = canvasBounds.y + canvasBounds.height - bounds.height; dy *= -1; } if (bounds.x < canvasBounds.x) { bounds.x = canvasBounds.x; dx *= -1; } if (bounds.y < canvasBounds.y) { bounds.y = canvasBounds.y; dy *= -1; } } @Override public void paint(Graphics2D g2d) { g2d.setColor(backColor); g2d.fill(copyBounds); g2d.setColor(foreground); g2d.draw(copyBounds); } } public static class DefaultAnimationModel implements AnimationModel { private List<Animatable> animatables; public DefaultAnimationModel() { animatables = new ArrayList<>(25); } @Override public synchronized void update(AnimationCanvas canvas, float progress) { for (Animatable animatable : animatables) { animatable.update(canvas, progress); } } @Override public synchronized void add(Animatable animatable) { animatables.add(animatable); } @Override public synchronized void remove(Animatable animatable) { animatables.remove(animatable); } @Override public synchronized Animatable[] getAnimatables() { return animatables.toArray(new Animatable[animatables.size()]); } } }

ACTUALIZAR

El mayor problema al que te enfrentarás es que la pantalla solo funciona en números enteros ...

private class Animator implements ActionListener { @Override public void actionPerformed(ActionEvent e) { squares[0].y += 1; squares[1].y += 1; repaint(); } }

Creo que ambas casillas en realidad están "temblorosas", pero debido a que el cuadro del dibujo tiene una falta tan obvia de cuerpo, se destaca más. Ejecuto esta prueba a aproximadamente 24 fps sin ningún problema.

Estoy haciendo una animación simple en Java y estoy tratando de hacerlo lo más suave posible.

Solo uso * .Doble clases internas de cada objeto Shape y configuro antialiasing en los objetos Graphics2D. Todo funciona siempre que use solo el método fill () pero si también uso el método draw () para dibujar líneas alrededor de la misma forma, la animación de estas líneas es discontinua, píxel por píxel.

Cada uno de mis rectángulos en el lienzo tiene este método para pintarse a sí mismo. Se mueve cada 20 ms y todo el lienzo se repinta utilizando Timer y TimerListener.

import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class AnimationTest { public static void main(String[] args) { JFrame frm = new JFrame("Test"); frm.setBounds(200, 200, 400, 400); frm.setResizable(false); frm.setLocationRelativeTo(null); AnimationCanvas a = new AnimationCanvas(); frm.add(a); frm.setVisible(true); a.startAnimation(); } } class AnimationCanvas extends JPanel { SimpleSquare[] squares = new SimpleSquare[2]; AnimationCanvas() { squares[0] = new SimpleSquare(50, 80, true); squares[1] = new SimpleSquare(160, 80, false); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); for (SimpleSquare c : squares) { c.paintSquare(g); } } Timer t; public void startAnimation() { t = new Timer(30, new Animator()); t.start(); } private class Animator implements ActionListener { @Override public void actionPerformed(ActionEvent e) { squares[0].y += 0.10; squares[1].y += 0.10; repaint(); } } } class SimpleSquare { double x; double y; Color color = Color.black; boolean fill; SimpleSquare(double x, double y, boolean fill) { this.x = x; this.y = y; this.fill = fill; } void paintSquare(Graphics g) { ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Shape s = new Rectangle.Double(x, y, 100, 100); g.setColor(color); ((Graphics2D) g).setStroke(new BasicStroke(2)); if (fill) { ((Graphics2D) g).fill(s); } else { ((Graphics2D) g).draw(s); } } }

Hay alguna manera de arreglar este problema? Miré alrededor por un buen rato.


Tienes:

private class Animator implements ActionListener { @Override public void actionPerformed(ActionEvent e) { squares[0].y += 0.10; squares[1].y += 0.10; repaint(); } }

Y

public void startAnimation() { t = new Timer(30, new Animator()); t.start(); }

Pero podrías lograr lo mismo cambiando a

private class Animator implements ActionListener { @Override public void actionPerformed(ActionEvent e) { squares[0].y += 1; squares[1].y += 1; repaint(); } }

Y

public void startAnimation() { t = new Timer(300, new Animator()); t.start(); }

Lo cual hace lo mismo, pero hace 10 veces menos bucles.

No probé el código, pero vale la pena intentarlo.

La solución adecuada es provista por MadProgrammer, donde mide la demora y las compensaciones.