Problemas con el método de pintura de Java, velocidad de actualización ridícula

Estoy desarrollando una versión muy simple de R-Type como trabajo para la universidad, pero a pesar de que funciona, la velocidad de la nave es muy lenta, por lo que el movimiento es feo y torpe. Uso el método repintar para actualizar la pantalla, ¿hay otros métodos o formas mejores que él?

Video del movimiento

Método de pintura en el panel principal

@Override public void paint(Graphics g) { super.paint(g); Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.drawImage(fondo, 0, 0,1200,600,this); pj.paint(g2); g2D=g2; } 

Método de pintura de PJ

 public void paint(Graphics2D g) { g.drawImage(imagen,x,y,this); } 

Método de movimiento de PJ

 public void move (KeyEvent e) { int dx = 0; int dy = 0; int code = e.getKeyCode(); switch (code) { case KeyEvent.VK_Q: dy-=1; break; case KeyEvent.VK_A: dy+=1; break; case KeyEvent.VK_P: dx+=1; break; case KeyEvent.VK_O: dx-=1; break; } int x = (getX()<maxX&&getX()!=0) ? getX()+dx : getX(); int y = (getY()=maxY||getY()==0) { if (dy==+1) y=y+1; } setPosicion(x, y); } 

  • El fondo imagen ya debería estar escalado a 1200×600.
  • No estoy seguro, pero es super.paint(g) necesario? También puede usar paintComponent .

El manejo del evento (parece que se mueve en 1 píxel al pulsar la tecla), debe hacerse correctamente. Yo habría establecido la dirección y la velocidad (1px), y dejarlo en un temporizador de swing para hacer el movimiento continuo.

Repintando mejor se hace resistente / flexible: repaint(20L) (50 cuadros por segundo); eventos como la reducción de teclado quizás con EventQueue.invokeLater(new Runnable() { ... }); .

Especialmente, puede usar volver a pintar con el área cambiada.

Puede encontrar un gran ejemplo de un progtwig similar aquí . El ejemplo demuestra la creación de un nuevo hilo y que ese hilo duerma cada iteración a través del ciclo principal.

Aquí hay otra pregunta sobre cómo cargar imágenes para juegos en Java.

Parece que el swing en sí es bastante malo para usar imágenes en los juegos. Es posible que desee considerar el uso de una biblioteca más adecuada.

A continuación se muestra un ejemplo simple utilizando un fondo como simple bucle de juego. Actualiza el estado de los objetos del juego y calcula la demora requerida para mantener los fps necesarios.

El objeto del juego ( Ship ) tiene la capacidad de acelerar / desacelerar en un corto período de tiempo

 import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.geom.Path2D; import javax.swing.AbstractAction; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.KeyStroke; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class AnimationTest { public static void main(String[] args) { new AnimationTest(); } public AnimationTest() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } JFrame frame = new JFrame("Test"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(new GamePane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class GamePane extends JPanel { private Ship ship; public GamePane() { ship = new Ship(); Thread thread = new Thread(new MainLoop(this)); thread.setDaemon(true); thread.start(); InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW); ActionMap am = getActionMap(); // Key controls... im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "upPressed"); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "downPressed"); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "upReleased"); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "downReleased"); am.put("upPressed", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { // Change the direction... ship.setDirection(-1); // Accelerate by 1 per frame ship.setVelocity(1); } }); am.put("downPressed", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { // Change direction ship.setDirection(1); // Accelerate by 1 per frame ship.setVelocity(1); } }); am.put("upReleased", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { // Deccelerate by 1 per frame ship.setVelocity(-1); } }); am.put("downReleased", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { // Deccelerate by 1 per frame ship.setVelocity(-1); } }); } @Override public Dimension getPreferredSize() { return new Dimension(200, 200); } public void updateState() { // Update the state of the game objects. // This would typically be better done in // some kind of model ship.update(getWidth(), getHeight()); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // Paint the game state... Graphics2D g2d = (Graphics2D) g.create(); ship.paint(g2d); g2d.dispose(); } } public class MainLoop implements Runnable { private GamePane pane; private int fps = 25; public MainLoop(GamePane pane) { this.pane = pane; } @Override public void run() { // Wait until the screen is ready while (pane.getHeight() <= 0) { try { Thread.sleep(125); } catch (InterruptedException ex) { } } // Main loop while (true) { // Start time loop long startTime = System.currentTimeMillis(); // Update the game state pane.updateState(); // Calculate the amount of time it took to update long elasped = System.currentTimeMillis() - startTime; // Calculate the number of milliseconds we need to sleep long sleep = Math.round((1000f / fps) - elasped); pane.repaint(); if (sleep > 0) { try { Thread.sleep(sleep); } catch (InterruptedException ex) { } } } } } public static class Ship { public static int MAX_SPEED = 8; private int direction = 0; private int velocity = 0; private int x; private int y; private int speed = 0; private Path2D shape; private boolean initState; public Ship() { shape = new Path2D.Float(); shape.moveTo(0, 0); shape.lineTo(5, 5); shape.lineTo(0, 10); shape.lineTo(0, 0); shape.closePath(); initState = true; } public void setDirection(int direction) { this.direction = direction; } public void setVelocity(int velocity) { this.velocity = velocity; } public void update(int width, int height) { if (initState) { y = (height - 10) / 2; initState = false; } else { // Add the velocity to the speed speed += velocity; // Don't over accelerate if (speed > MAX_SPEED) { speed = MAX_SPEED; } else if (speed < 0) { speed = 0; } // Adjust out position if we're moving if (speed > 0) { y += (direction * speed); } // Bounds check... if (y - 5 < 0) { y = 5; } else if (y + 5 > height) { y = height - 5; } } } public void paint(Graphics2D g2d) { int yPos = y - 5; g2d.translate(10, yPos); g2d.fill(shape); g2d.translate(-10, -yPos); } } }