简体   繁体   中英

Java 2D Game slowing down without apparent reason

i created a 2D platformer style game in java in which some parts are simualted by a physics-engine that i programmed myself as well. The problem is that the game slows down to around 30 fps from the intended 60 fps at some points. The game loop looks like this:

@Override
public void run() {

    init();

    long startTime;
    long elapsedTime;
    long waitTime;

    while(running) {

        startTime = System.nanoTime();

        update();
        long updateTime = (System.nanoTime() - startTime) / 1000000;
        draw();
        long drawTime = (System.nanoTime() - startTime) / 1000000;
        drawToScreen();
        long drawscreenTime = (System.nanoTime() - startTime) / 1000000;

        elapsedTime = System.nanoTime() - startTime;
        waitTime = targetTime - elapsedTime / 1000000;
        if(waitTime < 0) {
            waitTime = 0;
        }

        try {
            Thread.sleep(waitTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

I am measuring the time that is needed to update the game logic with updateTime and the time that is needed to draw with drawTime and drawscreenTime. At all times (during the slowdowns as well!) the updateTime is 0 ms and drawTime + drawscreenTime is around 10 ms, which is below the maximum of 16 ms (for 60 fps).

So far i have found out that changing the setup of potential collisions of my physical objects can reduce the slowdown. At first i wanted to seperate the objects by zones, so that the program doesnt need to check every object with every object. For example instead of 100x100 = 10000 objects only 33x33 + 33x33 + 33x33 = 3267 objects.

Seperating by zones looked like this:

private ArrayList<PhysicalObject> objects;
private ArrayList<ArrayList<PhysicalObject>> zones;

for(int i = 0; i < numberOfZones; i++) {
        zones.get(i).clear();
    }

    for(PhysicalObject object : objects) {
        for(int zone : object.getSimulationZones()) {
            zones.get(zone).add(object);
        }
    }

    for(ArrayList<PhysicalObject> list : zones) {

        for(PhysicalObject object1 : list) {
            for(PhysicalObject object2 : list) {

            collision stuff happens

            }
        }
    }
}

The "blunt" approach of chekcing every object with every other object looked like this:

private ArrayList<PhysicalObject> objects;

for(PhysicalObject object1 : objects) {
    for(PhysicalObject object2 : objects) {

        collision stuff happens
    }
}

Now as you might expect the blunt approach is slower when looking at updateTime, but when i use this approach the slowdowns are far less significant. I dont know how this is happening, since the time needed to do one game loop seems to be unrelated to the slowdowns, but when i change code that runs during the game loop, the slowdowns are varying.

At first i thought that Thread.sleep() might be at fault, since i heard that it is sometimes unreliable, so i tried using a Java.util.Timer. But when i use that with a delay of 16 ms (for 60 fps) the fps are jumping up and down around 30 fps.

I would really appreciate if someone could help me with this. If you need more Code, I can post more, but since the game-code is already pretty big I figured it would be best to start small and not flood my question even more.

EDIT:

It seems that drawing the game-level has an influence on the slowdowns as well. But instead of slowing down when there are more images to draw, it slows down when there are less images. But again: Even though the slowdowns are decreasing, the drawTime is increasing when there are more things to draw. The code for drawing the level looks like this:

public void draw(Graphics2D g2d) {

    for(int row = drawStartRow; row < drawStartRow + rowsToDraw; row++) {

        if(row >= numRows) {
            break;
        }

        for(int col = drawStartCol; col < drawStartCol + colsToDraw; col++) 
{

            if(col >= numCols) {
                break;
            }

            //0 stands for no tile to draw
            if(map[row][col] == 0) {
                continue;
            }

            int rc = map[row][col];
            int r = rc / numTilesAcross;
            int c = rc % numTilesAcross;

            g2d.drawImage(tiles[r][c].getImage(), (int)x + col * tileSize, (int)y + row * tileSize, null);
        }
    }
}

The images in the array were generated via BufferedImage.getSubImage() in the constructor.

EDIT 2:

I was finally able to get rid of the slowdowns by changing the drawing method of my level. Instead of iterating over every spot and drawing the corresponding image, i am now iterating over the different images first and then (for every image) over the spots and drawing them if they belong to the spot.

So overall my loop is times numberOfImages longer, but the slowdowns disappeared. The reason seems to be that I am now drawing every image as often as it needs to be drawn and then going on to the next image. But i dont understand how this is affecting my overall framerate so much, even though the time needed to draw is not changing?

As per the specs of drawImage : https://docs.oracle.com/javase/7/docs/api/java/awt/Graphics2D.html#drawImage(java.awt.Image,%20java.awt.geom.AffineTransform,%20java.awt.image.ImageObserver)

The drawing may be done asynchronously, which means that the image is not rendered when the function returns. By blocking the thread with Thread.sleep, you may be slowing the rendering. You can do a quick test to check if the image is fully rendered by checking the return value.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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