在每次渲染之前,BufferedImage不会被清除
我试图通过我正在观看的教程学习如何构build一个简单的游戏。 到目前为止,一切都很好,但是当我移动图像时,前面的图像不会被擦除或丢弃。 我不确定究竟是什么错,或为什么发生。 我有3个类,一个主类,玩家类和一个bufferimageloader类。
主要课程:
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; }
}
球员类别:
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类:
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; } }
这是当我启动和图像移动时得到的输出:
这是一个简单的Swing应用程序,名为Moving Eyes。 在GUI的绘图区域中移动光标时,GUI中的眼球将跟随鼠标光标。
我意识到这不是你想做的事情。 我提供了这个代码,所以你可以看到如何做一个简单的Swinganimation。 您可以使用此代码作为您自己的animation的基础。
这是Swing GUI。
创build此Swing GUI时,我使用了模型/视图/控制器模型 。 这意味着:
- 该视图可以从模型中读取值。
- 该视图可能不会更新模型。
- 控制器将更新模型。
- 控制器将重新绘制/重新validation视图。
基本上,模型是无知的观点和控制器。 这使您可以将视图和控制器从Swing更改为网站或Android应用程序。
模型/视图/控制器模式允许您一次关注Swing GUI的一部分。 一般来说,你将首先创build模型,然后是视图,最后是控制器。 您将不得不返回并向模型添加字段。
这里是代码:
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(); } } }
模型
Eye类是一个Java对象,它保存眼睛的起源(圆圈)和眼球的起源。 Eye类是这个简单例子中的模型。
视图
MovingEyes类是定义JFrame的类。 MovingEyes类是视图的一部分。 此类的主要方法调用SwingUtilities invokeLater方法,以确保Swing组件在Event Dispatch线程上定义和修改。
我们使用JFrame。 我们不扩展一个JFrame。 只有当你想要重写一个类方法的时候,你才能扩展一个Swing组件或者任何Java类。 当我谈论DrawingPanel时,我们会看到这个。
MovingEyes类的构造函数定义了Eye类的两个实例。 run方法定义了JFrame。 运行方法中的代码对于所有Swing GUI都是相似的。
DrawingPanel类构成了视图的其余部分。 DrawingPanel类扩展了JPanel,因为我们要覆盖paintComponent方法。 DrawingPanel类的构造函数设置绘图区的首选大小,并添加鼠标运动侦听器。 鼠标移动监听器是这个Swing GUI的控制器。
DrawingPanel类的paintComponent方法首先调用super paintComponent方法。 这将维护Swing paint链,并且应该始终是被覆盖的paintComponent方法的第一条语句。
DrawingPanel类的paintComponent方法中的其余代码绘制了眼睛。 我们只在paintComponent方法中绘制(绘制)代码。 控制码属于控制器。
调节器
EyeballListener类是控制器类。 在更复杂的Swing GUI中可以有多个控制器类。
EyeballListener类扩展了MouseMotionAdapter。 您可以实现MouseMotionListener。 我重写了一个方法,所以当我扩展MouseMotionAdapter时,代码更短。
EyeballListener类的mouseMoved方法在鼠标移动时触发MouseEvent。 我们通过find从眼睛中心到鼠标位置的θangular来计算眼球中心的新位置。 θangular用于计算眼球的新中心。
每个Eye实例在for循环中分别更新。 双眼更新后,绘图面板重新绘制。 这发生得如此之快,以至于在单独的线程中不需要animation循环。
animation循环更新模型,绘制视图,并等待指定的时间段。 您将为animation循环使用单独的线程,以便Event Dispatch线程上的GUI保持响应。 如果你的GUI没有响应,你可能在Event Dispatch线程上做了太多的工作。
你看了一下BufferStrategy的例子代码吗? https://docs.oracle.com/javase/7/docs/api/java/awt/image/BufferStrategy.html
您只需要在程序开始时创build一个BufferStrategy对象,而不是每个帧。 但是,旧图像不被删除的原因是因为你永远不会删除它。 你可以调用fillRect来做到这一点。