BufferedImage no se borra antes de cada renderizado

Estoy tratando de aprender a construir un juego simple a través de un tutorial que estoy viendo. Hasta ahora todo estaba bien, pero cuando muevo la imagen, la imagen anterior no se borra ni elimina. No estoy seguro exactamente qué está mal, o por qué está sucediendo. Tengo 3 clases, una clase principal, una clase de jugador y una clase bufferimageloader.

Clase principal:

import java.awt.Canvas; import java.awt.Graphics; import java.awt.image.BufferStrategy; import java.awt.image.BufferedImage; import java.io.IOException; import javax.swing.JFrame; public class Main extends Canvas implements Runnable { private boolean running = false; private Thread thread; private BufferedImage player; private Player p; public void init(){ // load and initiliaze BufferedImageLoader loader = new BufferedImageLoader(); try { player = loader.loadImage("/player_shotgun2.png"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } p = new Player(100, 100, this); } private synchronized void start(){ if(running) return; running = true; thread = new Thread(this); thread.start(); } private synchronized void stop(){ if(!running) return; running = false; try { thread.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.exit(1); } public void run() { init(); long lastTime = System.nanoTime(); final double amountOfTicks = 60.0; double ns = 1000000000 / amountOfTicks;// 1 second divided by 60, run 60 times per second double delta = 0; int updates = 0; int frames = 0; long timer = System.currentTimeMillis(); System.out.println("hi"); while(running){ long now = System.nanoTime(); delta += (now - lastTime) / ns; lastTime = now; if(delta >= 1){// delta = 1 = 1 second tick(); updates++; delta--; } render(); frames++; if(System.currentTimeMillis() - timer > 1000){ timer+= 1000; System.out.println(updates + " Ticks, Fps " + frames); updates = 0; frames = 0; } } stop(); } // Everything thats is updated in the game private void tick(){ p.tick(); } // Everything that is rendered in the game private void render(){ BufferStrategy bs = this.getBufferStrategy(); if(bs == null){ createBufferStrategy(3); return; } Graphics g = bs.getDrawGraphics(); ////////////////////////////// p.render(g); ////////////////////////////// g.dispose(); bs.show(); } public static void main(String[] args) { // TODO Auto-generated method stub Main main = new Main(); JFrame window = new JFrame(); window.setSize(500,600); window.setTitle("Zombie Game"); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.setVisible(true); window.add(main); main.start(); } public BufferedImage getPlayerImage(){ return player; } 

}

Clase de jugador:

 import java.awt.Graphics; import java.awt.image.BufferedImage; import javax.swing.JPanel; public class Player extends JPanel { private double x; private double y; public BufferedImage player; public Player(double x, double y, Main main){ this.x = x; this.y = y; player = main.getPlayerImage(); } public void tick(){ x++; } public void render(Graphics g){ super.paintComponent(g); g.drawImage(player, (int)x, (int)y, null); g.dispose(); } } 

Bufferedimageloader class:

 import java.awt.image.BufferedImage; import java.io.IOException; import javax.imageio.ImageIO; public class BufferedImageLoader { private BufferedImage image; public BufferedImage loadImage(String path) throws IOException{ image = ImageIO.read(getClass().getResource(path)); return image; } } 

Este es el resultado que obtengo cuando inicio y la imagen se mueve:

enter image description here

Esta es una aplicación de Swing simple llamada Moving Eyes. Los globos oculares en la GUI siguen el cursor del mouse mientras mueve el cursor en el área de dibujo de la GUI.

Me doy cuenta de que no está haciendo lo que quieres hacer. Proporciono este código para que pueda ver cómo hacer una animación Swing simple. Puede usar este código como base para hacer su propia animación.

Aquí está la GUI de Swing.

Mover los ojos

Usé el modelo / vista / modelo de controlador al crear esta GUI de Swing. Esto significa que:

  1. La vista puede leer valores del modelo.
  2. La vista puede no actualizar el modelo.
  3. El controlador actualizará el modelo.
  4. El controlador volverá a pintar / revalidar la vista.

Básicamente, el modelo ignora la vista y el controlador. Esto le permite cambiar la vista y el controlador de Swing a un sitio web o una aplicación de Android.

El patrón de modelo / vista / controlador le permite enfocarse en una parte de la GUI de Swing a la vez. En general, primero creará el modelo, luego la vista y finalmente los controladores. Tendrá que regresar y agregar campos al modelo.

Y aquí está el código:

 package com.ggl.testing; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Point; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; public class MovingEyes implements Runnable { private static final int drawingWidth = 400; private static final int drawingHeight = 400; private static final int eyeballHeight = 150; private static final int eyeballWidthMargin = 125; private DrawingPanel drawingPanel; private Eye[] eyes; private JFrame frame; public static void main(String[] args) { SwingUtilities.invokeLater(new MovingEyes()); } public MovingEyes() { this.eyes = new Eye[2]; this.eyes[0] = new Eye(new Point(eyeballWidthMargin, eyeballHeight)); this.eyes[1] = new Eye(new Point(drawingWidth - eyeballWidthMargin, eyeballHeight)); } @Override public void run() { frame = new JFrame("Moving Eyes"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); drawingPanel = new DrawingPanel(drawingWidth, drawingHeight); frame.add(drawingPanel); frame.pack(); frame.setLocationByPlatform(true); frame.setVisible(true); } public class DrawingPanel extends JPanel { private static final long serialVersionUID = -2977860217912678180L; private static final int eyeballOuterRadius = 50; private static final int eyeballInnerRadius = 20; public DrawingPanel(int width, int height) { this.addMouseMotionListener(new EyeballListener(this, eyeballOuterRadius - eyeballInnerRadius - 5)); this.setBackground(Color.WHITE); this.setPreferredSize(new Dimension(width, height)); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.BLACK); for (Eye eye : eyes) { drawCircle(g, eye.getOrigin(), eyeballOuterRadius); fillCircle(g, eye.getEyeballOrigin(), eyeballInnerRadius); } } private void drawCircle(Graphics g, Point origin, int radius) { g.drawOval(origin.x - radius, origin.y - radius, radius + radius, radius + radius); } private void fillCircle(Graphics g, Point origin, int radius) { g.fillOval(origin.x - radius, origin.y - radius, radius + radius, radius + radius); } } public class Eye { private final Point origin; private Point eyeballOrigin; public Eye(Point origin) { this.origin = origin; this.eyeballOrigin = origin; } public Point getEyeballOrigin() { return eyeballOrigin; } public void setEyeballOrigin(Point eyeballOrigin) { this.eyeballOrigin = eyeballOrigin; } public Point getOrigin() { return origin; } } public class EyeballListener extends MouseMotionAdapter { private final double eyeballDistance; private final DrawingPanel drawingPanel; public EyeballListener(DrawingPanel drawingPanel, double eyeballDistance) { this.drawingPanel = drawingPanel; this.eyeballDistance = eyeballDistance; } @Override public void mouseMoved(MouseEvent event) { Point p = event.getPoint(); for (Eye eye : eyes) { Point origin = eye.getOrigin(); double theta = Math.atan2((double) (py - origin.y), (double) (px - origin.x)); int x = (int) Math.round(Math.cos(theta) * eyeballDistance) + origin.x; int y = (int) Math.round(Math.sin(theta) * eyeballDistance) + origin.y; eye.setEyeballOrigin(new Point(x, y)); } drawingPanel.repaint(); } } } 

Modelo

La clase Eye es un objeto Java que contiene el origen del ojo (círculo) y el origen del globo ocular. La clase Eye es el modelo en este simple ejemplo.

Ver

La clase MovingEyes es la clase que define el JFrame. La clase MovingEyes es parte de la vista. El método principal de esta clase invoca el método invokeLater de SwingUtilities para garantizar que los componentes Swing se definan y modifiquen en el subproceso Event Dispatch.

Usamos un JFrame. No ampliamos un JFrame. La única vez que extiende un componente Swing, o cualquier clase Java, es cuando desea anular uno de los métodos de clase. Veremos esto cuando hable del DrawingPanel.

El constructor de la clase MovingEyes define 2 instancias de la clase Eye. El método de ejecución define el JFrame. El código en el método de ejecución será similar para todas las GUI de Swing.

La clase DrawingPanel constituye el rest de la vista. La clase DrawingPanel amplía JPanel porque queremos sobrescribir el método paintComponent. El constructor de la clase DrawingPanel establece el tamaño preferido del área de dibujo y agrega el detector de movimiento del mouse. El oyente de movimiento del mouse es el controlador de esta GUI de Swing.

El método paintComponent de la clase DrawingPanel primero llama al método super paintComponent. Esto mantiene la cadena de pintura Swing, y siempre debe ser la primera statement del método paintComponent sobrescrito.

El rest del código en el método paintComponent de la clase DrawingPanel atrae la atención. Solo tenemos código de dibujo (pintura) en el método paintComponent. El código de control pertenece al controlador.

Controlador

La clase EyeballListener es la clase de controlador. Puede tener más de una clase de controlador en una GUI Swing más complicada.

La clase EyeballListener extiende MouseMotionAdapter. Puede implementar MouseMotionListener. Estoy anulando un método, por lo que el código es más corto cuando extiendo el MouseMotionAdapter.

El método mouseMoved de la clase EyeballListener dispara un evento MouseEvent cuando se mueve el mouse. Calculamos una nueva posición para el centro de un globo ocular al encontrar el ángulo theta desde el centro del ojo hasta la posición del ratón. El ángulo theta se usa para calcular el nuevo centro del globo ocular.

Cada instancia de Eye se actualiza por separado en el ciclo for. Después de actualizar ambos ojos, se vuelve a pintar el panel de dibujo. Esto sucede tan rápido que no hay necesidad de un bucle de animación en un hilo separado.

Un bucle de animación actualiza el modelo, dibuja la vista y espera durante un período de tiempo especificado. Utilizaría un hilo separado para el bucle de animación, de modo que la GUI en el hilo de Despacho de evento permanezca receptiva. Si su GUI no responde, probablemente esté trabajando demasiado en el hilo de Despacho de evento.

¿Miraste el código de ejemplo de BufferStrategy? https://docs.oracle.com/javase/7/docs/api/java/awt/image/BufferStrategy.html

Solo necesita crear un objeto BufferStrategy al comienzo del progtwig, no en cada cuadro. Pero la razón por la cual su imagen anterior no se elimina es porque nunca la elimina. Puedes llamar a fillRect para hacer eso.