简体   繁体   English

Java:加快我的代码

[英]Java: Speed up my code

I have been coding an application which renders tiles and GUI and all that. 我一直在编写一个渲染图块和GUI的应用程序。 I seem to have come across a problem where my paintComponent seems to hog too much CPU and can no longer run much higher than 10 FPS on my small computer. 我似乎遇到了一个问题,我的paintComponent似乎占用了过多的CPU,并且在我的小型计算机上无法再以高于10 FPS的速度运行。 I was wondering if there was any more efficient way of running this code or threading it or anything to enhance the calculation speed. 我想知道是否有任何更有效的方式来运行此代码或对其进行线程化或任何其他方式来提高计算速度。 Here is my code: 这是我的代码:

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...");
}
}

Also I can tell it is the large loop which kills off about 200 FPS at this point because I commented that part off and my FPS shot up to about 250. 我还可以说这是一个大循环,这会杀死大约200 FPS,因为我评论了该部分,而我的FPS飙升至大约250。

Seems like you could multi-thread some of your outer for-loops at the top of the method, if the Graphics2D instance you are using is thread-safe. 如果您使用的Graphics2D实例是线程安全的,则似乎可以在方法顶部对某些外部for循环进行多线程处理。 Might be worthwhile to keep a ThreadPoolExecutor around for this and then break up your outer for-loops into instances of Runnable. 可能值得为此保留一个ThreadPoolExecutor,然后将外部for循环分解为Runnable实例。 This totally depends on whether or not the order of the draw matters to you - it's hard to tell just from the code you've posted. 这完全取决于抽签的顺序对您来说是否重要-仅从您发布的代码中很难分辨出来。

One other thing that jumps out at me is how you're accessing your 4-D image array. 让我惊讶的另一件事是您如何访问4-D图像阵列。 Recall that multi-dimensional Java arrays are actually arrays of references to other arrays. 回想一下,多维Java数组实际上是对其他数组的引用的数组。 You'd probably be better off getting a reference to a specific sub-array at the top of each loop, and accessing the sub-array reference that you've saved, rather than indexing the original array directly. 您最好在每个循环的顶部获取对特定子数组的引用,并访问已保存的子数组引用,而不是直接为原始数组建立索引。 This will save you a lot of unnecessary memory fetches. 这将节省大量不必要的内存获取。

As a general rule, try to precalculate as much as possible - eg the Strings that you are constructing. 作为一般规则,请尝试尽可能多地进行预先计算-例如,您正在构造的字符串。

Don't create a new Font on every frame, create it once and reuse it. 不要在每个框架上都创建一个新的Font ,只能创建一次并重复使用。

As the comments suggest, predraw tiles onto a reusable BufferedImage if that makes sense for your application. 正如评论所建议的,如果对您的应用程序有意义,则将图块预绘制到可重用的BufferedImage上。

You have quite a few common subexpressions, M0.gameFrame.getHeight() and (32 * zoom) , although the HotSpot compiler may well sort these out if you do repeated runs (a single frame test is no good). 您有很多常见的子表达式M0.gameFrame.getHeight()(32 * zoom) ,尽管如果您重复运行,HotSpot编译器很可能会解决这些问题(单帧测试是不好的)。 It will clarify the code if you factor these out, so is a good thing anyway. 如果您将这些因素排除在外,它将澄清代码,所以还是一件好事。

Beyond that, you need to do some profiling to see which parts are taking the most time... 除此之外,您还需要进行一些分析,以了解哪些部分花费了最多的时间...

Instead of building new strings (string concatenation) on every repaint, you could cache the strings and only rebuild them if data has changed. 您可以缓存字符串并仅在数据已更改时重建它们,而不是在每次重画时都构建新的字符串(字符串连接)。

Another idea: draw the static part of each string separately from the dynamic part of the string. 另一个想法:将每个字符串的静态部分与字符串的动态部分分开绘制。 You could use FontMetrics to determine the width of certain strings, to help you arrange the dynamic part next to the static part. 您可以使用FontMetrics确定某些字符串的宽度,以帮助您将动态部分排列在静态部分的旁边。

Also caching in general is good performance strategy which can be applied in many situations. 一般而言,缓存也是一种可以在许多情况下应用的良好性能策略。 See eg Bufferedimage . 参见例如Bufferedimage

It seems you redraw the entire screen from scratch each time paintComponent is called. 似乎每次调用paintComponent时都从头重新绘制了整个屏幕。

What would be better is to draw the initial blank image to an offscreen buffer. 最好将初始空白图像绘制到屏幕外缓冲区。 Each time a tile is updated mark it as dirty. 每次更新磁贴时,将其标记为脏。 When paintComponent is drawn only redraw the parts of the offscreen buffer which are out of date. 绘制paintComponent时,仅重绘屏幕外缓冲区中过期的部分。 That should save you a lot of effort. 那应该节省您很多精力。 Then just draw the entire buffer to the screen in one go. 然后只需将整个缓冲区一次绘制到屏幕上。 Drawing single large images is generally much quicker than drawing lots of small images. 绘制单个大图像通常比绘制许多小图像要快得多。

eg. 例如。

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);

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM