首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java动画内存问题

Java动画内存问题
EN

Stack Overflow用户
提问于 2016-01-11 23:35:18
回答 1查看 101关注 0票数 0

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

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

当我运行我的代码时,内存迅速增加,超过了4gb和15%的CPU使用率。请有人解释一下是什么原因造成了这个内存泄漏/问题。

请随时为我的绘画/图形留下推荐的替代方法。我为目前的代码如此混乱而道歉。

代码:

代码语言:javascript
复制
 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);
}

}

我的参考计划:参考图像

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-01-11 23:55:46

首先,看看在Swing中并发,可能还有如何使用摆动计时器

而且,您的paint方法非常危险,绘制是通过将一系列方法调用链接在一起来生成所需的结果来完成的,您不应该修改它,除非您非常、非常、非常肯定您正在做的事情。与其重写paint,不如简单地覆盖paintComponent (并调用它的超级方法)

有关详细信息,请参阅AWT和Swing中的绘画表演定制绘画

一般经验法则是,您应该有一个“模型”,它表示游戏的当前状态(就像玩家的位置和任何其他需要跟踪的值一样),您将有一个“游戏/主”循环,它将根据输入和其他所需的逻辑定期更新模型的状态,然后安排一次画图更新。

您正在绘制的绘画不应该包含任何逻辑,除了绘制模型当前状态所需的逻辑之外,绘画是用于绘画的

让我们看看密码..。

代码语言:javascript
复制
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();
}

这有点奇怪(有点危险)。由于JVM启动时调用main方法的方式是偶然的,所以循环没有在事件分派线程的上下文中运行,但是您不应该做这些假设。您应该使用Swing Timer或另一个Thread在其中运行此循环。

现在,有两个主要问题。

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

这似乎是你问题的核心根源。

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

一种更复杂的方法是使用BufferStrategyBufferStrategy和BufferCapabilities,这允许您控制绘画过程(AKA活动绘画),这将允许您在控制绘画过程时使用Thread

您还应该避免在可能的情况下使用staticstatic是造成更多问题和问题的原因,而不是我们从新开发人员那里看到的其他任何事情,它不是一种跨对象的通信机制。

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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34733102

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档