在每次渲染之前未清除BufferedImage

时间:2016-01-24 21:00:29

标签: java bufferedimage

我正在尝试通过我正在观看的教程学习如何构建一个简单的游戏。到目前为止一切都很好,但是当我移动图像时,前一个图像不会被删除或丢弃。我不确定到底是什么错,或者为什么会发生错误。我有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;
    }
}

这是我启动时获得的输出并且图像移动:

enter image description here

2 个答案:

答案 0 :(得分:4)

这是一个名为Moving Eyes的简单Swing应用程序。当您在GUI的绘图区域中移动光标时,GUI中的眼球会跟随鼠标光标。

我意识到它没有做你想做的事。我提供此代码,以便您可以看到如何进行简单的Swing动画。您可以使用此代码作为自己动画的基础。

这是Swing GUI。

Moving Eyes

我在创建这个Swing GUI时使用了model / view / controller model。这意味着:

  1. 视图可以从模型中读取值。
  2. 视图可能不会更新模型。
  3. 控制器将更新模型。
  4. 控制器将重新绘制/重新验证视图。
  5. 基本上,模型不了解视图和控制器。这允许您将视图和控制器从Swing更改为网站或Android应用程序。

    模型/视图/控制器模式允许您一次关注Swing GUI的一部分。通常,您将首先创建模型,然后创建视图,最后创建控制器。您将不得不返回并向模型添加字段。

    这是代码:

    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) (p.y - origin.y),
                            (double) (p.x - 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方法,以确保在Event Dispatch线程上定义和修改Swing组件。

    我们使用JFrame。我们不扩展JFrame。扩展Swing组件或任何Java类的唯一时间是要覆盖其中一个类方法。当我谈到DrawingPanel时,我们会看到这一点。

    MovingEyes类的构造函数定义了Eye类的2个实例。 run方法定义JFrame。对于所有Swing GUI,run方法中的代码都是类似的。

    DrawingPanel类构成视图的其余部分。 DrawingPanel类扩展了JPanel,因为我们想要覆盖paintComponent方法。 DrawingPanel类的构造函数设置绘图区域的首选大小,并添加鼠标移动侦听器。鼠标移动侦听器是此Swing GUI的控制器。

    DrawingPanel类的paintComponent方法首先调用super paintComponent方法。这样可以保持Swing绘制链,并且应该始终是覆盖的paintComponent方法的第一个语句。

    DrawingPanel类的paintComponent方法中的其余代码吸引眼球。我们在paintComponent方法中只有绘图(绘画)代码。控制代码属于控制器。

    控制器

    EyeballListener类是控制器类。在更复杂的Swing GUI中,您可以拥有多个控制器类。

    EyeballListener类扩展了MouseMotionAdapter。您可以实现MouseMotionListener。我正在覆盖一个方法,因此当我扩展MouseMotionAdapter时代码会更短。

    移动鼠标时,EyeballListener类的mouseMoved方法会触发MouseEvent。我们通过找到从眼睛中心到鼠标位置的θ角来计算眼球中心的新位置。 θ角用于计算眼球的新中心。

    每个Eye实例在for循环中单独更新。双眼更新后,绘图面板将重新绘制。这种情况发生得如此之快,以至于不需要在单独的线程中进行动画循环。

    动画循环更新模型,绘制视图,并等待指定的时间段。您将为动画循环使用单独的线程,以便Event Dispatch线程上的GUI保持响应。如果您的GUI没有响应,那么您可能在Event Dispatch线程上做了太多工作。

答案 1 :(得分:2)

您是否看过BufferStrategy的示例代码? https://docs.oracle.com/javase/7/docs/api/java/awt/image/BufferStrategy.html

您只需要在程序开头创建一个BufferStrategy对象,而不是每一帧。但是您的旧图像不会被删除的原因是因为您永远不会删除它。你可以调用fillRect来做到这一点。