Java动画内存问题

时间:2016-01-11 23:35:18

标签: java animation

我对这门语言有些陌生,但明年我将获得一个学位论文,其中包括一篇Java论文,所以我正在学习这门语言,这样明年我就不会完全没用了。

我正试图模仿游戏'Flappybird'(这是我想到的第一件需要Java图形的东西)。

当我运行我的代码时,内存会快速建立并达到超过4GB和15%的CPU使用率。有人可以解释导致此内存泄漏/问题的原因。

随意为我的绘画/图形留下推荐的替代方法。我为代码目前的不整洁而道歉。

CODE:

 import java.awt.Component;
 import java.awt.Image;
 import java.io.File;
 import java.io.IOException;
 import javax.imageio.ImageIO;
 import javax.swing.JFrame;


public class Animation {
private Image imgBackgroundBottom, imgBackgroundTop;
private int intFrameHeight;
private int intFrameWidth;

public static void main(String[] args) throws IOException {
    new Animation();
}

public Animation() throws IOException{
    System.out.println("Program Iniatiated");
    JFrame frameAnimation = new JFrame("Animation");
    frameAnimation.setResizable(false);
    frameAnimation.setSize(310, 650);       
    frameAnimation.setVisible(true);
    int Level = 10;
    frameAnimation.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    for (int i = 0; i < frameAnimation.getWidth() + 1; i++) {
        if (i == frameAnimation.getWidth()) {
            //Level--;
            i = 0;
        }
        frameAnimation.add(new AnimationPane(i));
        try {
            Thread.sleep(Level);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    frameAnimation.revalidate();
    }
}

import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Timer;
import java.util.concurrent.ThreadLocalRandom;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class AnimationPane extends JPanel {
      private BufferedImage imgBackgroundBottom, imgBackgroundTop,   imgPipeTop, imgPipeBottom;
      private int XPos = 40, YPos = 40;
      private int BackgroundTopWidth, BackgroundTopHeight;
      private int BackgroundBottomWidth, BackgroundBottomHeight;
      private int i = 0;
      public static int yPipe1, yPipe2, yPipe3, yPipe4;
      public static int xPipe1 = -50, xPipe2 = -50,  xPipe3 = -50, xPipe4 = -50;
      public static int runAgain = 1, selectPipe = 0;
      public AnimationPane(int xValue) {
             try {
                 if (imgBackgroundBottom = null ) {
                 imgBackgroundBottom = ImageIO.read(new File("resources/BackgroundBottom.png"));
                 imgBackgroundTop = ImageIO.read(new File("resources/BackgroundTop.png"));
                 imgPipeTop = ImageIO.read(new File("resources/PipeTop.png"));
                 imgPipeBottom = ImageIO.read(new File("resources/PipeBottom.png"));
                 BackgroundTopWidth = imgBackgroundTop.getWidth(null);
                 BackgroundBottomWidth = imgBackgroundBottom.getWidth(null);
                 BackgroundTopHeight = imgBackgroundTop.getHeight(null);
                 BackgroundBottomHeight = imgBackgroundBottom.getHeight(null);
                this.setDoubleBuffered(true);
                i = xValue;
                repaint();
           } catch (IOException e) {
                e.printStackTrace();
           }
      }

public void paintComponent(Graphics g){
    super.paintComponent(g);
    g.drawImage(imgBackgroundTop, 0, 0, getWidth(), BackgroundTopHeight * 2, this);
    //Level Difficult can be adjusted by adjusting thread.sleep count AND adjusting max.variance in randomisation.
    if (runAgain % 170 == 0) {
        selectPipe++;
        if (selectPipe % 4 == 0) {
            yPipe4 = ThreadLocalRandom.current().nextInt(50, 312 + 1);
            xPipe4 = 310;
        } else if (selectPipe % 3 == 0){
            yPipe3 = ThreadLocalRandom.current().nextInt(50, 312 + 1);
            xPipe3 = 310;
        } else if (selectPipe % 2 == 0) {
            yPipe2 = ThreadLocalRandom.current().nextInt(50, 312 + 1);
            xPipe2 = 310;
        } else {
            yPipe1 = ThreadLocalRandom.current().nextInt(50, 312 + 1);
            xPipe1 = 310;
        }
    }
    xPipe1--;
    xPipe2--;
    xPipe3--;
    xPipe4--;
    runAgain++;
    if (xPipe1 < 310  && xPipe1 > -50) {
        g.drawImage(imgPipeTop, xPipe1, yPipe1 - 400, 50, 400, this);
        g.drawImage(imgPipeBottom, xPipe1, yPipe1 + 150, 50, 400, this);
    }
    if (xPipe2 < 310 && xPipe2 > -50) {
        g.drawImage(imgPipeTop, xPipe2, yPipe2 - 400, 50, 400, this);
        g.drawImage(imgPipeBottom, xPipe2, yPipe2 + 150, 50, 400, this);
    }
    if (xPipe3 < 310 && xPipe3 > -50) {
        g.drawImage(imgPipeTop, xPipe3, yPipe3 - 400, 50, 400, this);
        g.drawImage(imgPipeBottom, xPipe3, yPipe3 + 150, 50, 400, this);
    }
    if (xPipe4 < 310 && xPipe4 > -50) {
        g.drawImage(imgPipeTop, xPipe4, yPipe4 - 400, 50, 400, this);
        g.drawImage(imgPipeBottom, xPipe4, yPipe4 + 150, 50, 400, this);
    }
    g.drawImage(imgBackgroundBottom, -i, BackgroundTopHeight * 2, getWidth() * 2, BackgroundBottomHeight * 2, this);
    g.drawImage(imgBackgroundBottom, getWidth() - i, BackgroundTopHeight * 2, getWidth() * 2, BackgroundBottomHeight * 2, this);
}

}

我的计划供参考: Reference Image

1 个答案:

答案 0 :(得分:1)

首先,请查看Concurrency in SwingHow to use Swing Timers

另外,你的id方法非常危险,绘画是通过将一系列方法调用链接在一起来生成所需的结果来完成的,你不应该修改它,除非你非常,非常,非常确定是什么你在干嘛您应该简单地覆盖id(并将其称为超级方法),而不是覆盖paint

有关详细信息,请参阅Painting in AWT and SwingPerforming Custom Painting

作为一般的经验法则,你应该有一个代表游戏当前状态的“模型”(比如玩家位置以及你需要跟踪的任何其他值),你会有一个“游戏/主游戏” “循环,它会定期更新模型的状态,根据所需的输入和其他逻辑,然后安排油漆更新。

你的绘画不应该包含绘制模型当前状态所需的任何逻辑,绘画用于绘画

我们来看看代码......

paint

这有点奇怪(而且有点危险)。由于JVM在启动时调用paintComponent方法的方式侥幸,因此循环不会在事件调度线程的上下文中运行,但是您永远不应该做出这些假设。您应该使用Swing for (int i = 0; i < frameAnimation.getWidth() + 1; i++) { if (i == frameAnimation.getWidth()) { //Level--; i = 0; } frameAnimation.add(new AnimationPane(i)); try { Thread.sleep(Level); } catch (InterruptedException e) { e.printStackTrace(); } frameAnimation.revalidate(); } 或其他main来运行此循环。

现在,有两个主要问题。

  1. 您正在循环的每次迭代中添加越来越多的面板
  2. 您正在从EDT上下文之外修改UI的状态,请参阅第一个链接。 Swing不是线程安全的,你不应该从EDT的上下文之外修改它的状态。
  3. 这似乎是您问题的核心来源

    就个人而言,我只有一个面板,它能够根据游戏的当前状态简单地绘制需要绘制的内容。请记住,Swing使用被动渲染引擎,因此可以随时触发绘制事件,这就是我们建议使用Swing Timer的原因。

    更复杂的方法是使用BufferStrategyBufferStrategy and BufferCapabilities,它允许您控制绘画过程(AKA主动绘画),这将允许您使用{{1相反,当你控制绘画过程时。

    你应该尽可能避免使用TimerThread是导致更多问题和问题的原因,然后是我们从新开发者那里看到的任何其他问题,它不是一个跨对象的通信机制。 / p>

    如果你需要共享数据(比如游戏循环和视图之间的模型),那么你应该将它的引用传递给两者