【问题标题】:Java: Speed up my codeJava:加速我的代码
【发布时间】:2012-03-13 21:57:29
【问题描述】:

我一直在编写一个应用程序,它可以渲染图块和 GUI 等等。我似乎遇到了一个问题,我的paintComponent 似乎占用了太多 CPU,并且在我的小型计算机上运行速度不再高于 10 FPS。我想知道是否有任何更有效的方法来运行此代码或线程化它或任何提高计算速度的方法。这是我的代码:

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class M1 extends JPanel implements Runnable {
public static double zoom = 1.25;
public static double charZoom = 1;
public static boolean breaking = false;

public void run() {

}

public void paintComponent(Graphics g) {
    super.paintComponent(g);
    if(zoom <= 0.03) zoom = 1.25;

    for(int cy = 0; cy < 3; cy++) {
        for(int cx = 0; cx < 3; cx++) {
            for(int y = 0; y < 16; y++) {
                for(int x = 0; x < 16; x++) {
                    g.drawImage(Tiles.tileImages.get(C0.chunk[x][y][cx][cy]), 
                            (int)((C0.cX[cx][cy] * C0.chunkWidth) * zoom) + ((int)(32 * zoom) * x) + 
                                ((M0.gameFrame.getWidth() / 2)) - (int)(PEntity.x.getValue() * zoom),
                            (int)((C0.cY[cx][cy] * C0.chunkHeight) * zoom) + ((int)(32 * zoom) * y) + 
                                ((M0.gameFrame.getHeight() / 2)) - (int)(PEntity.y.getValue() * zoom) + (int)(24.25 * zoom),// <-- 24.25 used to correctly position  charatcter
                            (int)(32 * zoom), (int)(32 * zoom), this);
                    if(C0.chunk[x][y][cx][cy].equals("a05")) {
                        g.drawImage(Tiles.treetop, 
                                (int)((C0.cX[cx][cy] * C0.chunkWidth) * zoom) + ((int)(32 * zoom) * x) + 
                                    ((M0.gameFrame.getWidth() / 2)) - (int)(PEntity.x.getValue() * zoom),
                                (int)((C0.cY[cx][cy] * C0.chunkHeight) * zoom) + ((int)(32 * zoom) * y) + 
                                    ((M0.gameFrame.getHeight() / 2)) - (int)(PEntity.y.getValue() * zoom) + (int)(24.25 * zoom)
                                    - (int) (32 * zoom),// <-- 24.25 used to correctly position  charatcter
                                (int)(32 * zoom), (int)(32 * zoom), this);
                    }
                }
            }
        }
    }

    if(breaking) {
        g.drawImage(M3.currentBreak, (int)((C0.cX[M3.cx][M3.cy] * C0.chunkWidth) * zoom) + ((int)(32 * zoom) * M3.x) + 
                ((M0.gameFrame.getWidth() / 2)) - (int)(PEntity.x.getValue() * zoom),
                (int)((C0.cY[M3.cx][M3.cy] * C0.chunkHeight) * zoom) + ((int)(32 * zoom) * M3.y) + 
                ((M0.gameFrame.getHeight() / 2)) - (int)(PEntity.y.getValue() * zoom) + (int)(24.25 * zoom),
                (int)(32 * zoom), (int)(32 * zoom), this);
    }

    M3.placeX = (48 * zoom);
    M3.placeY = (48 * zoom);

    if(M0.HUDenabled) {
        g.drawImage(PEntity.currentChar, 
                (M0.gameFrame.getWidth() / 2) - (int)((16 * charZoom) * zoom), 
                (M0.gameFrame.getHeight() / 2) - (int)((32 * charZoom) * zoom),
                (int)((32 * charZoom) * zoom), (int)((64 * charZoom) * zoom), this);

        g.setColor(Color.BLACK);
        g.setFont(new Font("Dialog", 1, 12));
        g.drawString("Terrem" + " By Tyler D :)", 5, 15);
        g.drawString("X: " + PEntity.x.getValue(), 5, 28);
        g.drawString("Y: " + PEntity.y.getValue(), 5, 41);
        g.drawString("ChunkX: " + C0.currentChunkX.getValue(), 5, 54);
        g.drawString("ChunkY: " + C0.currentChunkY.getValue(), 5, 67);
        g.drawString("BlockX: " + C0.currentBlockX.getValue(), 5, 80);
        g.drawString("BlockY: " + C0.currentBlockY.getValue(), 5, 93);
        g.drawString("Zoom: " + zoom, 5, 106);
        g.drawString(M4.tileArea[0][0] + "_" + M4.tileArea[1][0] + "_" + M4.tileArea[2][0], 5, 126);
        g.drawString(M4.tileArea[0][1] + "_" + M4.tileArea[1][1] + "_" + M4.tileArea[2][1], 5, 139);
        g.drawString(M4.tileArea[0][2] + "_" + M4.tileArea[1][2] + "_" + M4.tileArea[2][2], 5, 152);
        g.drawString("FPS: " + (int) FPS.currentFPS, 5, 172);

        //GUI
        g.drawImage(M0.GUIbar, (M0.gameFrame.getWidth() - (624)) / 2, (M0.gameFrame.getHeight() - 80), 624, 40, this);
        for(int i = 0; i < 9; i++) {
            g.drawImage(Item.Item_Img.get(PEntity.PInv[i]), ((M0.gameFrame.getWidth() - (624)) / 2) + 6 + (36 * i), 
                    (M0.gameFrame.getHeight() - 74), 28, 28, this);
            if(Item.Item_Img.get(PEntity.PInv[i]) != null) {
                g.drawString("" + (PEntity.stk[i] + 1), ((M0.gameFrame.getWidth() - (624)) / 2) + 6 + (36 * i), 
                        (M0.gameFrame.getHeight() - 47));
            }
        }
    }

    repaint();
    FPS.calculateFrameRate();
}

public M1() {
    M0.gameFrame.setVisible(true);
    Clock.Start();
    setBackground(new Color(242, 220, 121));
    System.out.println("M1 loaded...");
}
}

我还可以看出,这是一个大循环,此时杀死了大约 200 FPS,因为我评论了那部分,我的 FPS 飙升至大约 250。

【问题讨论】:

  • 删除所有字符串构建后它的运行速度有多快?
  • 格雷,他的大部分答案实际上都没有答案:S
  • 好吧,如果有人愿意帮助我,那么他们会帮助我。
  • RussS:最高可达 250 FPS
  • 为什么不将尽可能多的东西,尤其是任何背景图像预先绘制到 BufferedImage 上,然后通过 Graphics#drawImage(...) 方法调用在您的 paintComponent(...) 方法中显示 BufferedImage,然后只绘制在方法的其余部分发生变化的图像。

标签: java multithreading performance frame-rate paintcomponent


【解决方案1】:

如果您使用的 Graphics2D 实例是线程安全的,您似乎可以在方法顶部对一些外部 for 循环进行多线程处理。可能值得为此保留一个 ThreadPoolExecutor,然后将外部 for 循环分解为 Runnable 的实例。这完全取决于抽奖的顺序对您是否重要 - 仅从您发布的代码中很难分辨。

另外一件让我印象深刻的事情是您如何访问您的 4-D 图像阵列。回想一下,多维 Java 数组实际上是对其他数组的引用数组。您最好在每个循环的顶部获取对特定子数组的引用,并访问您保存的子数组引用,而不是直接索引原始数组。这将为您节省大量不必要的内存提取。

【讨论】:

  • 我怎样才能在 for 循环中穿线并仍然测试其中的所有内容?我以前从未听说过能够做到这一点......
  • 你必须实现 Runnable。在您的自定义 Runnable 实现中,您必须维护告诉 Runnable 它所代表的 for 循环的迭代的状态。将您的 Runnable 实例传递给新线程或由 ThreadPoolExecutor 管理的现有线程,您可以选择 - 您只需要确保等待这些线程完成您提交的每个 Runnable,这样您就可以保证在您的抽签时实际完成绘制方法返回。
【解决方案2】:

您可以缓存字符串并仅在数据发生更改时重新构建它们,而不是在每次重绘时构建新字符串(字符串连接)。

另一种思路:将每个字符串的静态部分与字符串的动态部分分开绘制。您可以使用 FontMetrics 来确定某些字符串的宽度,以帮助您将动态部分排列在静态部分旁边。

一般来说,缓存也是一种很好的性能策略,可以在许多情况下应用。参见例如Bufferedimage.

【讨论】:

  • 我怎样才能缓存字符串而不是重建它们?
【解决方案3】:

作为一般规则,请尝试尽可能多地预先计算 - 例如您正在构建的字符串。

不要在每一帧上都创建一个新的Font,创建一次并重复使用它。

正如 cmets 建议的那样,如果这对您的应用程序有意义,请在可重复使用的 BufferedImage 上预绘制图块。

您有很多常见的子表达式,M0.gameFrame.getHeight()(32 * zoom),尽管如果您重复运行,HotSpot 编译器可能可以很好地解决这些问题(单帧测试不好)。如果您将这些因素考虑在内,它将澄清代码,所以无论如何都是一件好事。

除此之外,您还需要进行一些分析以查看哪些部分花费的时间最多...

【讨论】:

  • 好主意,我已经尝试了其中的大部分,但是用于预绘制它的 BufferedImage 比循环和其他所有内容更能破坏我的 FPS。
  • 这可能会有所帮助,我会尝试一下。感谢您的帮助
【解决方案4】:

似乎每次调用paintComponent 时您都从头开始重绘整个屏幕。

最好将初始空白图像绘制到屏幕外缓冲区。每次更新磁贴时,将其标记为脏。绘制paintComponent 时,仅重绘屏幕外缓冲区中过期的部分。这应该可以为您节省很多精力。然后只需一次将整个缓冲区绘制到屏幕上。绘制单个大图像通常比绘制大量小图像要快得多。

例如。

public void paintComponent(Graphics g) {

    updateOffscreenBuffer(); 
    // ^-- contains all the nested for loops, but does minimal work

    g.drawImage(getOffscreenBuffer());
    // ^-- draw the entire buffer all in one go to the screen

    drawHUD(g);

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-14
    • 2011-01-27
    • 2011-08-05
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多