Java: ускорить мой код - PullRequest
0 голосов
/ 14 марта 2012

Я кодирую приложение, которое рендерит тайлы, графический интерфейс и все такое. Кажется, я столкнулся с проблемой, когда мой paintComponent, кажется, перегружает процессор и больше не может работать на моем маленьком компьютере намного быстрее, чем 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.

Ответы [ 4 ]

2 голосов
/ 14 марта 2012

Похоже, вы могли бы многопоточность некоторых из ваших внешних циклов for в верхней части метода, если экземпляр Graphics2D, который вы используете, является потокобезопасным.Возможно, для этого стоит сохранить ThreadPoolExecutor и разбить ваши внешние циклы for на экземпляры Runnable.Это полностью зависит от того, важен ли для вас порядок розыгрыша - трудно судить только из кода, который вы опубликовали.

Еще одна вещь, которая бросается в глаза, - это то, как вы получаете доступ к своемуМассив 4-х изображений.Напомним, что многомерные массивы Java на самом деле являются массивами ссылок на другие массивы.Возможно, вам лучше получить ссылку на конкретный подмассив в верхней части каждого цикла и получить доступ к сохраненной ссылке на подмассив, а не напрямую индексировать исходный массив.Это сэкономит вам массу ненужных выборок из памяти.

1 голос
/ 14 марта 2012

Кажется, вы перерисовываете весь экран с нуля каждый раз, когда вызывается 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);

}
1 голос
/ 14 марта 2012

Вместо создания новых строк (конкатенации строк) при каждом перерисовке, вы можете кэшировать строки и восстанавливать их только в случае изменения данных.

Другая идея: рисовать статическую часть каждой строки отдельно от динамической части строки.Вы можете использовать FontMetrics для определения ширины определенных строк, чтобы помочь вам расположить динамическую часть рядом со статической частью.

Кроме того, кэширование в целом является хорошей стратегией производительности, которая может применяться во многих ситуациях.См. Например Bufferedimage.

1 голос
/ 14 марта 2012

Как правило, старайтесь максимально рассчитать как можно больше - например, строки, которые вы строите.

Не создавайте новый Font в каждом кадре, создайте его один раз и используйте его повторно.

Как следует из комментариев, предварительно перерисовывайте плитки в многократно используемый BufferedImage, если это имеет смысл для вашего приложения.

У вас довольно много общих подвыражений, M0.gameFrame.getHeight() и (32 * zoom), хотя HotSpotкомпилятор может хорошо разобраться в этом, если вы делаете повторные прогоны (тест с одним кадром не годится).Он прояснит код, если вы их учтете, так что в любом случае это хорошо.

Помимо этого, вам нужно выполнить некоторое профилирование, чтобы увидеть, какие части занимают больше всего времени ...

...